1
0
Fork 0
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:
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.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

View file

@ -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
*/

View file

@ -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

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.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$

View file

@ -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) {
}
}