1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-07 00:05:53 +02:00

Bug 414831 - Preserve line delimiters when writing xml

Change-Id: Ib6c023fb73193f3b15450f52cefcc67068d1398d
Also-by: Andrew Gvozdev <angvoz.dev@gmail.com>
Signed-off-by: Andrew Gvozdev <angvoz.dev@gmail.com>
Signed-off-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/17187
This commit is contained in:
Andrew Gvozdev 2013-10-10 16:44:12 -04:00 committed by Marc-Andre Laperle
parent b36cb27a1e
commit 35b004c5d4
5 changed files with 179 additions and 65 deletions

View file

@ -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.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.core.settings.model.ICSettingEntry;
import org.eclipse.cdt.internal.core.XmlUtil; 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.CConfigurationSpecSettings;
import org.eclipse.cdt.internal.core.settings.model.IInternalCCfgInfo; import org.eclipse.cdt.internal.core.settings.model.IInternalCCfgInfo;
import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages; import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages;
@ -557,7 +558,11 @@ public class LanguageSettingsProvidersSerializer {
try { try {
serializingLockWsp.acquire(); 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 // manufacture events while inside the lock
events = createLanguageSettingsChangeEvents(broadcastingWorkspaceProviders); events = createLanguageSettingsChangeEvents(broadcastingWorkspaceProviders);
} finally { } finally {
@ -881,7 +886,11 @@ public class LanguageSettingsProvidersSerializer {
if (isWorkspaceStoreEmpty) { if (isWorkspaceStoreEmpty) {
new java.io.File(uriStoreWsp).delete(); new java.io.File(uriStoreWsp).delete();
} else { } 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 // manufacture the event only if serialization was successful

View file

@ -16,6 +16,7 @@
package org.eclipse.cdt.internal.core.model; package org.eclipse.cdt.internal.core.model;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; 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.IFileInfo;
import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IPath;
@ -413,6 +415,9 @@ public class Util implements ICLogConstants {
return null; return null;
} }
/**
* Returns value of line separator preference from the given preference node.
*/
private static String getLineSeparatorFromPreferences(Preferences node) { private static String getLineSeparatorFromPreferences(Preferences node) {
try { try {
// be careful looking up for our node so not to create any nodes as side effect // be careful looking up for our node so not to create any nodes as side effect
@ -425,38 +430,21 @@ 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.
* will be the first available value from the list below:
* <ol>
* <li> Line separator currently used in that file.
* <li> Line separator defined in project preferences.
* <li> Line separator defined in instance preferences.
* <li> Line separator defined in default preferences.
* <li> Operating system default line separator.
* </ol>
* @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 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(IFile file) { public static String getLineSeparator(URI fileUri) {
if (file.exists()) { String value = null;
InputStream input = null; InputStream input = null;
try { try {
input = file.getContents(); java.io.File file = new java.io.File(fileUri);
int c = input.read(); if (file.exists()) {
while (c != -1 && c != '\r' && c != '\n') input = new FileInputStream(file);
c = input.read(); value = getLineSeparator(input);
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) { } catch (Exception e) {
// ignore
} catch (IOException e) {
// ignore // ignore
} finally { } finally {
try { try {
@ -466,13 +454,79 @@ public class Util implements ICLogConstants {
//ignore //ignore
} }
} }
return value;
} }
Preferences rootNode = Platform.getPreferencesService().getRootNode();
/**
* 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; 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:
* <ol>
* <li> Line separator defined in project preferences.
* <li> Line separator defined in instance preferences.
* <li> Line separator defined in default preferences.
* <li> Operating system default line separator.
* </ol>
*
* @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 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 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 (project != null) {
value = getLineSeparatorFromPreferences(rootNode.node(ProjectScope.SCOPE).node(project.getName()));
if (value != null) if (value != null)
return value; 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:
* <ol>
* <li> Line separator defined in instance preferences.
* <li> Line separator defined in default preferences.
* <li> Operating system default line separator.
* </ol>
* @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;
// try with instance preferences // try with instance preferences
value = getLineSeparatorFromPreferences(rootNode.node(InstanceScope.SCOPE)); value = getLineSeparatorFromPreferences(rootNode.node(InstanceScope.SCOPE));
if (value != null) if (value != null)
@ -485,6 +539,31 @@ public class Util implements ICLogConstants {
return LINE_SEPARATOR; 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 * Return true if the file is not a directory and has length > 0
*/ */

View file

@ -121,7 +121,6 @@ public class XmlProjectDescriptionStorage extends AbstractCProjectDescriptionSto
static final String CONFIGURATION = "cconfiguration"; //$NON-NLS-1$ 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 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) { public XmlProjectDescriptionStorage(CProjectDescriptionStorageTypeProxy type, IProject project, Version version) {
super(type, project, version); super(type, project, version);
@ -573,12 +572,8 @@ public class XmlProjectDescriptionStorage extends AbstractCProjectDescriptionSto
// Get the ProjectDescription as a utf-8 string // Get the ProjectDescription as a utf-8 string
stream = write(element); stream = write(element);
utfString = stream.toString("UTF-8"); //$NON-NLS-1$ utfString = stream.toString("UTF-8"); //$NON-NLS-1$
String eol = Util.getLineSeparator(projectFile);
// Make sure we keep the same line separator if the file exists utfString = XmlUtil.replaceLineSeparatorInternal(utfString, eol);
// 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);
} finally { } finally {
if (stream != null) if (stream != null)
stream.close(); // Cleanup the stream stream.close(); // Cleanup the stream

View file

@ -35,6 +35,7 @@ import org.eclipse.cdt.core.errorparsers.ErrorParserNamedWrapper;
import org.eclipse.cdt.core.errorparsers.RegexErrorParser; import org.eclipse.cdt.core.errorparsers.RegexErrorParser;
import org.eclipse.cdt.core.errorparsers.RegexErrorPattern; import org.eclipse.cdt.core.errorparsers.RegexErrorPattern;
import org.eclipse.cdt.internal.core.XmlUtil; 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.filesystem.URIUtil;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement; 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) { } catch (Exception e) {
throw new CoreException(CCorePlugin.createStatus("Failed serializing to file " + STORAGE_ERRORPARSER_EXTENSIONS, e)); //$NON-NLS-1$ throw new CoreException(CCorePlugin.createStatus("Failed serializing to file " + STORAGE_ERRORPARSER_EXTENSIONS, e)); //$NON-NLS-1$

View file

@ -17,6 +17,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI; import java.net.URI;
import javax.xml.parsers.DocumentBuilder; 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.CCorePlugin;
import org.eclipse.cdt.core.resources.ResourcesUtil; 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.IFile;
import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
@ -45,8 +47,10 @@ import org.w3c.dom.NodeList;
* *
*/ */
public class XmlUtil { 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 EOL_XML = "\n"; //$NON-NLS-1$
private static final String DEFAULT_IDENT = "\t"; //$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. * Convenience method to create new XML DOM Document.
@ -267,28 +271,26 @@ public class XmlUtil {
* *
* @param doc - DOM Document to serialize. * @param doc - DOM Document to serialize.
* @param uriLocation - URI of the file. * @param uriLocation - URI of the file.
* @param lineSeparator - line separator.
*
* @throws IOException in case of problems with file I/O * @throws IOException in case of problems with file I/O
* @throws TransformerException in case of problems with XML output * @throws TransformerException in case of problems with XML output
*/ */
public static void serializeXml(Document doc, URI uriLocation) throws IOException, TransformerException { public static void serializeXml(Document doc, URI uriLocation, String lineSeparator) throws IOException, TransformerException, CoreException {
XmlUtil.prettyFormat(doc); XmlUtil.prettyFormat(doc);
java.io.File storeFile = new java.io.File(uriLocation); java.io.File storeFile = new java.io.File(uriLocation);
if (!storeFile.exists()) { if (!storeFile.exists()) {
storeFile.createNewFile(); storeFile.createNewFile();
} }
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
XmlUtil.prettyFormat(doc); String utfString = new String(toByteArray(doc), ENCODING_UTF_8);
DOMSource source = new DOMSource(doc); utfString = XmlUtil.replaceLineSeparatorInternal(utfString, lineSeparator);
StreamResult result = new StreamResult(getFileOutputStreamWorkaround(storeFile));
transformer.transform(source, result);
result.getOutputStream().close(); FileOutputStream output = getFileOutputStreamWorkaround(storeFile);
output.write(utfString.getBytes(ENCODING_UTF_8));
output.close();
ResourcesUtil.refreshWorkspaceFiles(uriLocation); ResourcesUtil.refreshWorkspaceFiles(uriLocation);
} }
@ -338,7 +340,7 @@ public class XmlUtil {
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
Transformer transformer = TransformerFactory.newInstance().newTransformer(); Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ transformer.setOutputProperty(OutputKeys.ENCODING, ENCODING_UTF_8);
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
DOMSource source = new DOMSource(doc); DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(stream); StreamResult result = new StreamResult(stream);
@ -350,6 +352,23 @@ public class XmlUtil {
} }
} }
/**
* <b>Do not use outside of CDT.</b> 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.<br/> * Serialize XML Document into a workspace file.<br/>
* Note: clients should synchronize access to this method. * Note: clients should synchronize access to this method.
@ -361,12 +380,19 @@ public class XmlUtil {
public static void serializeXml(Document doc, IFile file) throws CoreException { public static void serializeXml(Document doc, IFile file) throws CoreException {
XmlUtil.prettyFormat(doc); XmlUtil.prettyFormat(doc);
InputStream input = new ByteArrayInputStream(toByteArray(doc)); 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()) { if (file.exists()) {
file.setContents(input, IResource.FORCE, null); file.setContents(input, IResource.FORCE, null);
} else { } else {
file.create(input, IResource.FORCE, null); file.create(input, IResource.FORCE, null);
} }
} catch (UnsupportedEncodingException e) {
}
} }
/** /**