mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-05 15:25:49 +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:
parent
b36cb27a1e
commit
35b004c5d4
5 changed files with 179 additions and 65 deletions
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
* <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 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:
|
||||
* <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;
|
||||
// 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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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$
|
||||
|
|
|
@ -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;i<attrLen;i+=2) {
|
||||
String attrName = attributes[i];
|
||||
String attrValue = attributes[i+1];
|
||||
|
@ -267,28 +271,26 @@ public class XmlUtil {
|
|||
*
|
||||
* @param doc - DOM Document to serialize.
|
||||
* @param uriLocation - URI of the file.
|
||||
* @param lineSeparator - line separator.
|
||||
*
|
||||
* @throws IOException in case of problems with file I/O
|
||||
* @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);
|
||||
|
||||
java.io.File storeFile = new java.io.File(uriLocation);
|
||||
if (!storeFile.exists()) {
|
||||
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);
|
||||
DOMSource source = new DOMSource(doc);
|
||||
StreamResult result = new StreamResult(getFileOutputStreamWorkaround(storeFile));
|
||||
transformer.transform(source, result);
|
||||
String utfString = new String(toByteArray(doc), ENCODING_UTF_8);
|
||||
utfString = XmlUtil.replaceLineSeparatorInternal(utfString, lineSeparator);
|
||||
|
||||
result.getOutputStream().close();
|
||||
FileOutputStream output = getFileOutputStreamWorkaround(storeFile);
|
||||
output.write(utfString.getBytes(ENCODING_UTF_8));
|
||||
|
||||
output.close();
|
||||
ResourcesUtil.refreshWorkspaceFiles(uriLocation);
|
||||
}
|
||||
|
||||
|
@ -338,7 +340,7 @@ public class XmlUtil {
|
|||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
Transformer transformer = TransformerFactory.newInstance().newTransformer();
|
||||
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$
|
||||
DOMSource source = new DOMSource(doc);
|
||||
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/>
|
||||
* 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) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue