Copyright (c) 2005, 2015 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2005, 2015 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.ltk.internal.core.refactoring.history; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TimeZone; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.ltk.core.refactoring.IRefactoringCoreStatusCodes; import org.eclipse.ltk.core.refactoring.RefactoringContribution; import org.eclipse.ltk.core.refactoring.RefactoringCore; import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringDescriptorProxy; import org.eclipse.ltk.core.refactoring.RefactoringSessionDescriptor; import org.eclipse.ltk.core.refactoring.history.RefactoringHistory; import org.eclipse.ltk.internal.core.refactoring.BasicElementLabels; import org.eclipse.ltk.internal.core.refactoring.IRefactoringSerializationConstants; import org.eclipse.ltk.internal.core.refactoring.Messages; import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages; import org.eclipse.ltk.internal.core.refactoring.RefactoringCorePlugin; import org.eclipse.ltk.internal.core.refactoring.RefactoringSessionReader; import org.eclipse.ltk.internal.core.refactoring.RefactoringSessionTransformer;
Manager for persistable refactoring histories.
Since:3.2
/** * Manager for persistable refactoring histories. * * @since 3.2 */
public final class RefactoringHistoryManager {
The index component delimiter
/** The index component delimiter */
public static final char DELIMITER_COMPONENT= '\t';
The index entry delimiter
/** The index entry delimiter */
public static final char DELIMITER_ENTRY= '\n';
The calendar instance
/** The calendar instance */
private static final Calendar fgCalendar= Calendar.getInstance(TimeZone.getTimeZone("GMT+00:00")); //$NON-NLS-1$
Checks whether the argument map is well-formed.

All arguments contained in the map are checked according to the rules of RefactoringDescriptor.

Params:
  • arguments – the argument map
Throws:
  • CoreException – if the argument violates any of the constraints
/** * Checks whether the argument map is well-formed. * <p> * All arguments contained in the map are checked according to the rules of * {@link RefactoringDescriptor}. * </p> * * @param arguments * the argument map * @throws CoreException * if the argument violates any of the constraints */
public static void checkArgumentMap(final Map<String, String> arguments) throws CoreException { Assert.isNotNull(arguments); for (final Iterator<? extends Entry<?, ?>> iterator= arguments.entrySet().iterator(); iterator.hasNext();) { final Entry<?, ?> entry= iterator.next(); if (entry.getKey() instanceof String) { final String string= (String) entry.getKey(); final char[] characters= string.toCharArray(); if (characters.length == 0) { throw new CoreException(new Status(IStatus.ERROR, RefactoringCore.ID_PLUGIN, IRefactoringCoreStatusCodes.REFACTORING_HISTORY_FORMAT_ERROR, RefactoringCoreMessages.RefactoringHistoryManager_empty_argument, null)); } for (int index= 0; index < characters.length; index++) { if (Character.isWhitespace(characters[index])) throw new CoreException(new Status(IStatus.ERROR, RefactoringCore.ID_PLUGIN, IRefactoringCoreStatusCodes.REFACTORING_HISTORY_FORMAT_ERROR, RefactoringCoreMessages.RefactoringHistoryManager_whitespace_argument_key, null)); } } else { throw new CoreException(new Status(IStatus.ERROR, RefactoringCore.ID_PLUGIN, IRefactoringCoreStatusCodes.REFACTORING_HISTORY_FORMAT_ERROR, Messages.format(RefactoringCoreMessages.RefactoringHistoryManager_non_string_argument, entry.getKey()), null)); } if (! (entry.getValue() instanceof String)) { throw new CoreException(new Status(IStatus.ERROR, RefactoringCore.ID_PLUGIN, IRefactoringCoreStatusCodes.REFACTORING_HISTORY_FORMAT_ERROR, Messages.format(RefactoringCoreMessages.RefactoringHistoryManager_non_string_value, entry.getKey()), null)); } } }
Creates a new core exception representing an I/O error.
Params:
  • exception – the throwable to wrap
Returns:the core exception
/** * Creates a new core exception representing an I/O error. * * @param exception * the throwable to wrap * @return the core exception */
private static CoreException createCoreException(final Throwable exception) { return new CoreException(new Status(IStatus.ERROR, RefactoringCore.ID_PLUGIN, IRefactoringCoreStatusCodes.REFACTORING_HISTORY_IO_ERROR, exception.getLocalizedMessage(), exception)); }
Escapes the specified string for the history index.
Params:
  • string – the string for the history index
Returns:the escaped string
/** * Escapes the specified string for the history index. * * @param string * the string for the history index * @return the escaped string */
public static String escapeString(final String string) { if (string.indexOf(DELIMITER_COMPONENT) < 0) { final int length= string.length(); final StringBuilder buffer= new StringBuilder(length + 16); for (int index= 0; index < length; index++) { final char character= string.charAt(index); if (DELIMITER_COMPONENT == character) buffer.append(DELIMITER_COMPONENT); buffer.append(character); } return buffer.toString(); } return string; }
Returns the argument map of the specified descriptor.
Params:
  • descriptor – the refactoring descriptor
Returns:the argument map, or null
/** * Returns the argument map of the specified descriptor. * * @param descriptor * the refactoring descriptor * @return the argument map, or <code>null</code> */
public static Map<String, String> getArgumentMap(final RefactoringDescriptor descriptor) { Map<String, String> arguments= null; final RefactoringContribution contribution= RefactoringContributionManager.getInstance().getRefactoringContribution(descriptor.getID()); if (contribution != null) arguments= contribution.retrieveArgumentMap(descriptor); else if (descriptor instanceof DefaultRefactoringDescriptor) arguments= ((DefaultRefactoringDescriptor) descriptor).getArguments(); return arguments; }
Reads refactoring descriptor proxies.
Params:
  • store – the file store to read
  • project – the name of the project, or null for the workspace
  • collection – the collection of proxies to fill in
  • start – the start time stamp, inclusive
  • end – the end time stamp, inclusive
  • monitor – the progress monitor to use
  • task – the task label to use
Throws:
/** * Reads refactoring descriptor proxies. * * @param store * the file store to read * @param project * the name of the project, or <code>null</code> for the * workspace * @param collection * the collection of proxies to fill in * @param start * the start time stamp, inclusive * @param end * the end time stamp, inclusive * @param monitor * the progress monitor to use * @param task * the task label to use * @throws CoreException * if an error occurs */
private static void readRefactoringDescriptorProxies(final IFileStore store, final String project, final Collection<RefactoringDescriptorProxy> collection, final long start, final long end, final IProgressMonitor monitor, final String task) throws CoreException { try { monitor.beginTask(RefactoringCoreMessages.RefactoringHistoryService_retrieving_history, 22); final IFileInfo info= store.fetchInfo(EFS.NONE, new SubProgressMonitor(monitor, 2, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); if (!info.isDirectory() && info.exists() && store.getName().equalsIgnoreCase(RefactoringHistoryService.NAME_INDEX_FILE)) { try (InputStream stream= store.openInputStream(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL))) { final RefactoringDescriptorProxy[] proxies= readRefactoringDescriptorProxies(stream, project, start, end); collection.addAll(Arrays.asList(proxies)); monitor.worked(1); } catch (IOException exception) { throw createCoreException(exception); } finally { monitor.worked(1); } } else monitor.worked(4); if (monitor.isCanceled()) throw new OperationCanceledException(); final IFileStore[] stores= store.childStores(EFS.NONE, new SubProgressMonitor(monitor, 2, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); final IProgressMonitor subMonitor= new SubProgressMonitor(monitor, 12); try { subMonitor.beginTask(task, stores.length); for (IFileStore s : stores) { readRefactoringDescriptorProxies(s, project, collection, start, end, new SubProgressMonitor(subMonitor, 1), task); } } finally { subMonitor.done(); } } finally { monitor.done(); } }
Reads refactoring descriptor proxies from the specified input stream.

The refactoring descriptor proxies are returned in no particular order.

Params:
  • stream – the input stream where to read from
  • project – the name of the project, or null for the workspace
  • start – the start time stamp, inclusive
  • end – the end time stamp, inclusive
Throws:
Returns:An array of refactoring descriptor proxies
/** * Reads refactoring descriptor proxies from the specified input stream. * <p> * The refactoring descriptor proxies are returned in no particular order. * </p> * * @param stream * the input stream where to read from * @param project * the name of the project, or <code>null</code> for the * workspace * @param start * the start time stamp, inclusive * @param end * the end time stamp, inclusive * @return An array of refactoring descriptor proxies * @throws IOException * if an input/output error occurs */
public static RefactoringDescriptorProxy[] readRefactoringDescriptorProxies(final InputStream stream, final String project, final long start, final long end) throws IOException { final List<DefaultRefactoringDescriptorProxy> list= new ArrayList<>(); final BufferedReader reader= new BufferedReader(new InputStreamReader(stream, IRefactoringSerializationConstants.OUTPUT_ENCODING)); while (reader.ready()) { final String line= reader.readLine(); if (line != null) { final int index= line.indexOf(DELIMITER_COMPONENT); if (index > 0) { try { final long stamp= Long.valueOf(line.substring(0, index)).longValue(); if (stamp >= start && stamp <= end) list.add(new DefaultRefactoringDescriptorProxy(unescapeString(line.substring(index + 1)), project, stamp)); } catch (NumberFormatException exception) { // Just skip } } } } return list.toArray(new RefactoringDescriptorProxy[list.size()]); }
Reads default refactoring descriptors from the specified input stream.

The refactoring descriptors are returned in no particular order.

Params:
  • stream – the input stream where to read from
Throws:
  • CoreException – if an error occurs while reading the descriptors
Returns:An array of refactoring descriptor proxies
/** * Reads default refactoring descriptors from the specified input stream. * <p> * The refactoring descriptors are returned in no particular order. * </p> * * @param stream * the input stream where to read from * @return An array of refactoring descriptor proxies * @throws CoreException * if an error occurs while reading the descriptors */
public static DefaultRefactoringDescriptor[] readRefactoringDescriptors(final InputStream stream) throws CoreException { final List<RefactoringDescriptor> list= new ArrayList<>(64); readRefactoringDescriptors(stream, list, new NullProgressMonitor()); return list.toArray(new DefaultRefactoringDescriptor[list.size()]); }
Reads default refactoring descriptors from the specified input stream.
Params:
  • stream – the input stream where to read from
  • collection – the list of descriptors read from the history
  • monitor – the progress monitor to use
Throws:
  • CoreException – if an error occurs while reading the descriptors
/** * Reads default refactoring descriptors from the specified input stream. * * @param stream * the input stream where to read from * @param collection * the list of descriptors read from the history * @param monitor * the progress monitor to use * @throws CoreException * if an error occurs while reading the descriptors */
private static void readRefactoringDescriptors(final InputStream stream, final Collection<RefactoringDescriptor> collection, final IProgressMonitor monitor) throws CoreException { try { monitor.beginTask(RefactoringCoreMessages.RefactoringHistoryService_retrieving_history, 1); final RefactoringDescriptor[] results= new RefactoringSessionReader(true, null).readSession(new InputSource(new BufferedInputStream(stream))).getRefactorings(); for (RefactoringDescriptor result : results) { collection.add(result); } } finally { monitor.done(); } }
Removes the refactoring history index tree spanned by the specified file store.
Params:
  • store – the file store spanning the history index tree
  • monitor – the progress monitor to use
  • task – the task label to use
Throws:
  • CoreException – if an error occurs while removing the index tree
/** * Removes the refactoring history index tree spanned by the specified file * store. * * @param store * the file store spanning the history index tree * @param monitor * the progress monitor to use * @param task * the task label to use * @throws CoreException * if an error occurs while removing the index tree */
private static void removeIndexTree(final IFileStore store, final IProgressMonitor monitor, final String task) throws CoreException { try { monitor.beginTask(task, 16); final IFileInfo info= store.fetchInfo(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); if (info.isDirectory()) { if (info.getName().equalsIgnoreCase(RefactoringHistoryService.NAME_HISTORY_FOLDER)) return; final IFileStore[] stores= store.childStores(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); final IProgressMonitor subMonitor= new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL); try { subMonitor.beginTask(RefactoringCoreMessages.RefactoringHistoryService_updating_history, stores.length); for (IFileStore s : stores) { final IFileInfo current= s.fetchInfo(EFS.NONE, new SubProgressMonitor(subMonitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); if (current.isDirectory()) { final char[] characters= s.getName().toCharArray(); for (int offset= 0; offset < characters.length; offset++) { if (Character.isDigit(characters[offset])) return; else continue; } } } } finally { subMonitor.done(); } } final IFileStore parent= store.getParent(); store.delete(0, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); removeIndexTree(parent, new SubProgressMonitor(monitor, 12, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), task); } finally { monitor.done(); } }
Sorts the refactoring descriptor proxies in ascending order of their time stamps.
Params:
  • descriptors – the refactoring descriptors
/** * Sorts the refactoring descriptor proxies in ascending order of their time * stamps. * * @param descriptors * the refactoring descriptors */
public static void sortRefactoringDescriptorsAscending(final RefactoringDescriptor[] descriptors) { Arrays.sort(descriptors, new Comparator<RefactoringDescriptor>() { @Override public int compare(RefactoringDescriptor first, RefactoringDescriptor second) { long delta= first.getTimeStamp() - second.getTimeStamp(); if (delta > 0) return 1; else if (delta < 0) return -1; return 0; } }); }
Sorts the refactoring descriptor proxies in ascending order of their time stamps.
Params:
  • proxies – the refactoring descriptor proxies
/** * Sorts the refactoring descriptor proxies in ascending order of their time * stamps. * * @param proxies * the refactoring descriptor proxies */
public static void sortRefactoringDescriptorsAscending(final RefactoringDescriptorProxy[] proxies) { Arrays.sort(proxies, new Comparator<RefactoringDescriptorProxy>() { @Override public int compare(RefactoringDescriptorProxy first, RefactoringDescriptorProxy second) { long delta= first.getTimeStamp() - second.getTimeStamp(); if (delta > 0) return 1; else if (delta < 0) return -1; return 0; } }); }
Sorts the refactoring descriptor proxies in descending order of their time stamps.
Params:
  • proxies – the refactoring descriptor proxies
/** * Sorts the refactoring descriptor proxies in descending order of their * time stamps. * * @param proxies * the refactoring descriptor proxies */
public static void sortRefactoringDescriptorsDescending(final RefactoringDescriptorProxy[] proxies) { Arrays.sort(proxies, new Comparator<RefactoringDescriptorProxy>() { @Override public int compare(RefactoringDescriptorProxy first, RefactoringDescriptorProxy second) { long delta= second.getTimeStamp() - first.getTimeStamp(); if (delta > 0) return 1; else if (delta < 0) return -1; return 0; } }); }
Returns a path representing the history part for the specified time stamp.
Params:
  • stamp – the time stamp
Returns:A path representing the folder of the history part
/** * Returns a path representing the history part for the specified time * stamp. * * @param stamp * the time stamp * @return A path representing the folder of the history part */
public static IPath stampToPath(final long stamp) { fgCalendar.setTimeInMillis(stamp); final StringBuilder buffer= new StringBuilder(256); buffer.append(fgCalendar.get(Calendar.YEAR)); buffer.append(IPath.SEPARATOR); buffer.append(fgCalendar.get(Calendar.MONTH) + 1); buffer.append(IPath.SEPARATOR); buffer.append(fgCalendar.get(Calendar.WEEK_OF_YEAR)); return new Path(buffer.toString()); }
Transforms the specified refactoring descriptor into a DOM node.
Params:
  • descriptor – the descriptor to transform
  • projects – true to include project information, false otherwise
Throws:
  • CoreException – if an error occurs while transforming the descriptor
Returns:the DOM node representing the refactoring descriptor
/** * Transforms the specified refactoring descriptor into a DOM node. * * @param descriptor * the descriptor to transform * @param projects * <code>true</code> to include project information, * <code>false</code> otherwise * @return the DOM node representing the refactoring descriptor * @throws CoreException * if an error occurs while transforming the descriptor */
private static Document transformDescriptor(final RefactoringDescriptor descriptor, final boolean projects) throws CoreException { final RefactoringSessionTransformer transformer= new RefactoringSessionTransformer(projects); try { transformer.beginSession(null, IRefactoringSerializationConstants.CURRENT_VERSION); try { final String id= descriptor.getID(); transformer.beginRefactoring(id, descriptor.getTimeStamp(), descriptor.getProject(), descriptor.getDescription(), descriptor.getComment(), descriptor.getFlags()); final Map<String, String> arguments= getArgumentMap(descriptor); if (arguments != null) { checkArgumentMap(arguments); for (final Iterator<Entry<String, String>> iterator= arguments.entrySet().iterator(); iterator.hasNext();) { final Entry<String, String> entry= iterator.next(); transformer.createArgument(entry.getKey(), entry.getValue()); } } } finally { transformer.endRefactoring(); } } finally { transformer.endSession(); } return transformer.getResult(); }
Un-escapes the specified string from the history index.
Params:
  • string – the string from the history index
Returns:the un-escaped string
/** * Un-escapes the specified string from the history index. * * @param string * the string from the history index * @return the un-escaped string */
public static String unescapeString(final String string) { if (string.indexOf(DELIMITER_COMPONENT) < 0) { final int length= string.length(); final StringBuilder buffer= new StringBuilder(length); for (int index= 0; index < length; index++) { final char character= string.charAt(index); if (DELIMITER_COMPONENT == character) { if (index < length - 1) { final char escape= string.charAt(index + 1); if (DELIMITER_COMPONENT == escape) continue; } } buffer.append(character); } return buffer.toString(); } return string; }
Writes the specified index entry to the refactoring history.
Params:
  • file – the history index file
  • proxies – the refactoring descriptors
  • flags – the flags to use (either EFS.NONE or EFS.APPEND)
  • monitor – the progress monitor to use
  • task – the task label
Throws:
/** * Writes the specified index entry to the refactoring history. * * @param file * the history index file * @param proxies * the refactoring descriptors * @param flags * the flags to use (either {@link EFS#NONE} or * {@link EFS#APPEND}) * @param monitor * the progress monitor to use * @param task * the task label * @throws CoreException * if an error occurs while writing the index entry * @throws IOException * if an input/output error occurs */
private static void writeIndexEntry(final IFileStore file, final RefactoringDescriptorProxy[] proxies, final int flags, final IProgressMonitor monitor, final String task) throws CoreException, IOException { OutputStream output= null; try { monitor.beginTask(task, 2); file.getParent().mkdir(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); output= new BufferedOutputStream(file.openOutputStream(flags, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL))); writeRefactoringDescriptorProxies(output, proxies); } finally { monitor.done(); if (output != null) { try { output.close(); } catch (IOException exception) { // Do nothing } } } }
Writes refactoring descriptor proxies to the specified output stream.
Params:
  • stream – the output stream where to write to
  • proxies – the refactoring descriptor proxies to write
Throws:
/** * Writes refactoring descriptor proxies to the specified output stream. * * @param stream * the output stream where to write to * @param proxies * the refactoring descriptor proxies to write * @throws IOException * if an input/output error occurs */
public static void writeRefactoringDescriptorProxies(final OutputStream stream, final RefactoringDescriptorProxy[] proxies) throws IOException { final StringBuilder buffer= new StringBuilder(proxies.length * 64); sortRefactoringDescriptorsAscending(proxies); for (RefactoringDescriptorProxy proxy : proxies) { buffer.append(proxy.getTimeStamp()); buffer.append(DELIMITER_COMPONENT); buffer.append(escapeString(proxy.getDescription())); buffer.append(DELIMITER_ENTRY); } stream.write(buffer.toString().getBytes(IRefactoringSerializationConstants.OUTPUT_ENCODING)); }
A simple XML writer. Using this instead of the javax.xml.transform classes allows compilation against JCL Foundation (bug 80053).
/** * A simple XML writer. Using this instead of the javax.xml.transform classes allows * compilation against JCL Foundation (bug 80053). */
private static final class DOMWriter extends PrintWriter { /* constants */ private static final String XML_VERSION= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; //$NON-NLS-1$
Creates a new DOM writer on the given output writer.
Params:
  • output – the output writer
/** * Creates a new DOM writer on the given output writer. * * @param output the output writer */
public DOMWriter(Writer output) { super(output); } public void printDocument(Document doc) { println(XML_VERSION); printElement(doc.getDocumentElement()); }
Prints the given element.
Params:
  • element – the element to print
/** * Prints the given element. * * @param element the element to print */
public void printElement(Element element) { // Ensure extra whitespace is not emitted next to a Text node, // as that will result in a situation where the restored text data is not the // same as the saved text data. boolean hasChildren= element.hasChildNodes(); startTag(element, hasChildren); if (hasChildren) { boolean prevWasText= false; NodeList children= element.getChildNodes(); for (int i= 0; i < children.getLength(); i++) { Node node= children.item(i); if (node instanceof Element) { if (!prevWasText) { println(); } printElement((Element) children.item(i)); prevWasText= false; } else if (node instanceof Text) { print(getEscaped(node.getNodeValue())); prevWasText= true; } } if (!prevWasText) { println(); } endTag(element); } } private void startTag(Element element, boolean hasChildren) { StringBuilder sb= new StringBuilder(); sb.append("<"); //$NON-NLS-1$ sb.append(element.getTagName()); NamedNodeMap attributes= element.getAttributes(); for (int i= 0; i < attributes.getLength(); i++) { Attr attribute= (Attr) attributes.item(i); sb.append(" "); //$NON-NLS-1$ sb.append(attribute.getName()); sb.append("=\""); //$NON-NLS-1$ sb.append(getEscaped(String.valueOf(attribute.getValue()))); sb.append("\""); //$NON-NLS-1$ } sb.append(hasChildren ? ">" : "/>"); //$NON-NLS-1$ //$NON-NLS-2$ print(sb.toString()); } private void endTag(Element element) { StringBuilder sb= new StringBuilder(); sb.append("</"); //$NON-NLS-1$ sb.append(element.getNodeName()); sb.append(">"); //$NON-NLS-1$ print(sb.toString()); } private static void appendEscapedChar(StringBuilder buffer, char c) { String replacement= getReplacement(c); if (replacement != null) { buffer.append('&'); buffer.append(replacement); buffer.append(';'); } else { buffer.append(c); } } private static String getEscaped(String s) { StringBuilder result= new StringBuilder(s.length() + 10); for (int i= 0; i < s.length(); ++i) { appendEscapedChar(result, s.charAt(i)); } return result.toString(); } private static String getReplacement(char c) { // Encode special XML characters into the equivalent character references. // The first five are defined by default for all XML documents. // The next three (#xD, #xA, #x9) are encoded to avoid them // being converted to spaces on deserialization // (fixes bug 93720) switch (c) { case '<': return "lt"; //$NON-NLS-1$ case '>': return "gt"; //$NON-NLS-1$ case '"': return "quot"; //$NON-NLS-1$ case '\'': return "apos"; //$NON-NLS-1$ case '&': return "amp"; //$NON-NLS-1$ case '\r': return "#x0D"; //$NON-NLS-1$ case '\n': return "#x0A"; //$NON-NLS-1$ case '\u0009': return "#x09"; //$NON-NLS-1$ } return null; } }
Writes refactoring session descriptor to the specified output stream.
Params:
  • stream – the output stream where to write to
  • descriptor – the refactoring session descriptors to write
  • stamps – true to write time stamps as well, false otherwise
Throws:
  • CoreException – if an error occurs while writing the refactoring session descriptor
/** * Writes refactoring session descriptor to the specified output stream. * * @param stream * the output stream where to write to * @param descriptor * the refactoring session descriptors to write * @param stamps * <code>true</code> to write time stamps as well, * <code>false</code> otherwise * @throws CoreException * if an error occurs while writing the refactoring session * descriptor */
public static void writeRefactoringSession(final OutputStream stream, final RefactoringSessionDescriptor descriptor, final boolean stamps) throws CoreException { final RefactoringSessionTransformer transformer= new RefactoringSessionTransformer(true); final RefactoringDescriptor[] descriptors= descriptor.getRefactorings(); try { transformer.beginSession(descriptor.getComment(), descriptor.getVersion()); for (RefactoringDescriptor current : descriptors) { if (current != null) { try { long stamp= stamps ? current.getTimeStamp() : -1; transformer.beginRefactoring(current.getID(), stamp, current.getProject(), current.getDescription(), current.getComment(), current.getFlags()); final Map<String, String> arguments= getArgumentMap(current); if (arguments != null) { checkArgumentMap(arguments); for (final Iterator<Entry<String, String>> iterator= arguments.entrySet().iterator(); iterator.hasNext();) { final Entry<String, String> entry= iterator.next(); transformer.createArgument(entry.getKey(), entry.getValue()); } } } finally { transformer.endRefactoring(); } } } } finally { transformer.endSession(); } final Document result= transformer.getResult(); writeNode(stream, result); } private static void writeNode(final OutputStream stream, Document document) { OutputStreamWriter outputStreamWriter= new OutputStreamWriter(stream, Charset.forName("UTF-8")); //$NON-NLS-1$ @SuppressWarnings("resource") DOMWriter writer= new DOMWriter(outputStreamWriter); writer.printDocument(document); writer.flush(); }
The cached session descriptor, or null
/** The cached session descriptor, or <code>null</code> */
private RefactoringSessionDescriptor fCachedDescriptor= null;
The cached document, or null
/** The cached document, or <code>null</code> */
private Document fCachedDocument= null;
The cached path, or null
/** The cached path, or <code>null</code> */
private IPath fCachedPath= null;
The cached file store, or null
/** The cached file store, or <code>null</code> */
private IFileStore fCachedStore= null;
The history file store
/** The history file store */
private final IFileStore fHistoryStore;
The non-empty name of the managed project, or null for the workspace
/** * The non-empty name of the managed project, or <code>null</code> for the * workspace */
private final String fProjectName;
Creates a new refactoring history manager.
Params:
  • store – the history file store
  • name – the non-empty name of the managed project, or null for the workspace
/** * Creates a new refactoring history manager. * * @param store * the history file store * @param name * the non-empty name of the managed project, or * <code>null</code> for the workspace */
RefactoringHistoryManager(final IFileStore store, final String name) { Assert.isNotNull(store); Assert.isTrue(name == null || !"".equals(name)); //$NON-NLS-1$ fHistoryStore= store; fProjectName= name; }
Adds the specified refactoring descriptor to the refactoring history.
Params:
  • descriptor – the refactoring descriptor to add
  • sort – true if the refactoring descriptor should be inserted into the history according to its time stamp, false if the descriptor is assumed to be the most recent one, and its simply appended
  • monitor – the progress monitor to use
Throws:
  • CoreException – if an error occurs while adding the descriptor to the history
/** * Adds the specified refactoring descriptor to the refactoring history. * * @param descriptor * the refactoring descriptor to add * @param sort * <code>true</code> if the refactoring descriptor should be * inserted into the history according to its time stamp, * <code>false</code> if the descriptor is assumed to be the * most recent one, and its simply appended * @param monitor * the progress monitor to use * @throws CoreException * if an error occurs while adding the descriptor to the history */
void addRefactoringDescriptor(final RefactoringDescriptor descriptor, final boolean sort, final IProgressMonitor monitor) throws CoreException { try { monitor.beginTask(RefactoringCoreMessages.RefactoringHistoryService_updating_history, 18); final long stamp= descriptor.getTimeStamp(); if (stamp >= 0) { final IPath path= stampToPath(stamp); final IFileStore folder= fHistoryStore.getFileStore(path); final IFileStore history= folder.getChild(RefactoringHistoryService.NAME_HISTORY_FILE); final IFileStore index= folder.getChild(RefactoringHistoryService.NAME_INDEX_FILE); final RefactoringDescriptorProxy[] proxies= new RefactoringDescriptorProxy[] { new DefaultRefactoringDescriptorProxy(descriptor.getDescription(), descriptor.getProject(), descriptor.getTimeStamp())}; if (history.fetchInfo(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)).exists()) { InputStream input= null; try { input= new BufferedInputStream(history.openInputStream(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL))); final Document document= getCachedDocument(path, input); try { input.close(); input= null; } catch (IOException exception) { // Do nothing } monitor.worked(1); final Document result= transformDescriptor(descriptor, false); if (result != null) { boolean found= false; final NodeList list= result.getElementsByTagName(IRefactoringSerializationConstants.ELEMENT_REFACTORING); final Element root= document.getDocumentElement(); if (sort) { final String string= Long.toString(stamp); for (int offset= 0; offset < list.getLength(); offset++) { final Element element= (Element) list.item(offset); final String attribute= element.getAttribute(IRefactoringSerializationConstants.ATTRIBUTE_STAMP); if (attribute != null) { if (string.compareTo(attribute) > 0) { root.insertBefore(document.importNode(element, true), element); found= true; break; } } } } if (!found) root.appendChild(document.importNode(list.item(0), true)); writeHistoryEntry(history, document, new SubProgressMonitor(monitor, 10, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), RefactoringCoreMessages.RefactoringHistoryService_updating_history); if (sort) { final Set<RefactoringDescriptorProxy> set= new HashSet<>(64); readRefactoringDescriptorProxies(index, null, set, 0, Long.MAX_VALUE, new SubProgressMonitor(monitor, 2), RefactoringCoreMessages.RefactoringHistoryService_updating_history); writeIndexEntry(index, set.toArray(new RefactoringDescriptorProxy[set.size()]), EFS.NONE, new SubProgressMonitor(monitor, 3, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), RefactoringCoreMessages.RefactoringHistoryService_updating_history); } else writeIndexEntry(index, proxies, EFS.APPEND, new SubProgressMonitor(monitor, 5, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), RefactoringCoreMessages.RefactoringHistoryService_updating_history); } } catch (ParserConfigurationException | IOException | SAXException exception) { throw createCoreException(exception); } finally { if (input != null) { try { input.close(); } catch (IOException exception) { // Do nothing } } } } else { try { final Document result= transformDescriptor(descriptor, false); writeHistoryEntry(history, result, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), RefactoringCoreMessages.RefactoringHistoryService_updating_history); writeIndexEntry(index, proxies, EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), RefactoringCoreMessages.RefactoringHistoryService_updating_history); } catch (IOException exception) { throw createCoreException(exception); } } } } finally { monitor.done(); } }
Returns the cached refactoring history document.
Params:
  • path – the path of the document
  • input – the input stream where to read the document
Throws:
Returns:the cached refactoring history document
/** * Returns the cached refactoring history document. * * @param path * the path of the document * @param input * the input stream where to read the document * @return the cached refactoring history document * @throws SAXException * if an error occurs while parsing the history entry * @throws IOException * if an input/output error occurs * @throws ParserConfigurationException * if an error occurs in the parser configuration */
private Document getCachedDocument(final IPath path, final InputStream input) throws SAXException, IOException, ParserConfigurationException { if (path.equals(fCachedPath) && fCachedDocument != null) return fCachedDocument; DocumentBuilder parser= DocumentBuilderFactory.newInstance().newDocumentBuilder(); parser.setErrorHandler(new DefaultHandler()); final Document document= parser.parse(new InputSource(input)); fCachedDocument= document; fCachedPath= path; return document; }
Returns the cached refactoring session descriptor.
Params:
  • store – the file store of the descriptor
  • projectName – project name, or null for the workspace
  • input – the input stream where to read the descriptor
Throws:
Returns:the cached refactoring session descriptor
/** * Returns the cached refactoring session descriptor. * * @param store * the file store of the descriptor * @param projectName * project name, or <code>null</code> for the workspace * @param input * the input stream where to read the descriptor * @return the cached refactoring session descriptor * @throws CoreException * if an error occurs while reading the session */
private RefactoringSessionDescriptor getCachedSession(final IFileStore store, String projectName, final InputStream input) throws CoreException { if (store.equals(fCachedStore) && fCachedDescriptor != null) return fCachedDescriptor; final RefactoringSessionDescriptor descriptor; try { descriptor= new RefactoringSessionReader(false, projectName).readSession(new InputSource(input)); fCachedDescriptor= descriptor; fCachedStore= store; return descriptor; } catch (CoreException e) { throw new CoreException(new MultiStatus( RefactoringCorePlugin.getPluginId(), IRefactoringCoreStatusCodes.REFACTORING_HISTORY_IO_ERROR, new IStatus[] { e.getStatus() }, Messages.format(RefactoringCoreMessages.RefactoringHistoryManager_error_reading_file, BasicElementLabels.getURLPart(store.toURI().toString())), null)); } }
Reads the refactoring history from disk.
Params:
  • start – the start time stamp, inclusive
  • end – the end time stamp, inclusive
  • monitor – the progress monitor to use
Returns:the refactoring history
/** * Reads the refactoring history from disk. * * @param start * the start time stamp, inclusive * @param end * the end time stamp, inclusive * @param monitor * the progress monitor to use * @return the refactoring history */
RefactoringHistory readRefactoringHistory(final long start, final long end, final IProgressMonitor monitor) { try { monitor.beginTask(RefactoringCoreMessages.RefactoringHistoryService_retrieving_history, 200); final Set<RefactoringDescriptorProxy> set= new HashSet<>(); try { if (fHistoryStore.fetchInfo(EFS.NONE, new SubProgressMonitor(monitor, 20, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)).exists()) readRefactoringDescriptorProxies(fHistoryStore, fProjectName, set, start, end, new SubProgressMonitor(monitor, 80), RefactoringCoreMessages.RefactoringHistoryService_retrieving_history); final IFileStore store= EFS.getLocalFileSystem().getStore(RefactoringCorePlugin.getDefault().getStateLocation()).getChild(RefactoringHistoryService.NAME_HISTORY_FOLDER).getChild(RefactoringHistoryService.NAME_WORKSPACE_PROJECT); if (store.fetchInfo(EFS.NONE, new SubProgressMonitor(monitor, 20, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)).exists()) readRefactoringDescriptorProxies(store, null, set, start, end, new SubProgressMonitor(monitor, 80), RefactoringCoreMessages.RefactoringHistoryService_retrieving_history); } catch (CoreException exception) { RefactoringCorePlugin.log(exception); } final RefactoringDescriptorProxy[] proxies= new RefactoringDescriptorProxy[set.size()]; set.toArray(proxies); return new RefactoringHistoryImplementation(proxies); } finally { monitor.done(); } }
Removes refactoring descriptors from the managed history.

All refactoring descriptors must be from the history entry denoted by the specified path.

Params:
  • proxies – the refactoring descriptors
  • path – the path of the history entry
  • monitor – the progress monitor to use
  • task – the task label to use
Throws:
/** * Removes refactoring descriptors from the managed history. * <p> * All refactoring descriptors must be from the history entry denoted by the * specified path. * </p> * * @param proxies * the refactoring descriptors * @param path * the path of the history entry * @param monitor * the progress monitor to use * @param task * the task label to use * @throws CoreException * if an error occurs */
private void removeRefactoringDescriptors(final RefactoringDescriptorProxy[] proxies, final IPath path, final IProgressMonitor monitor, final String task) throws CoreException { try { monitor.beginTask(task, 5); final IFileStore folder= fHistoryStore.getFileStore(path); final IFileStore index= folder.getChild(RefactoringHistoryService.NAME_INDEX_FILE); if (index.fetchInfo(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)).exists()) { final Set<RefactoringDescriptorProxy> resultingProxies= new HashSet<>(64); readRefactoringDescriptorProxies(index, null, resultingProxies, 0, Long.MAX_VALUE, new SubProgressMonitor(monitor, 1), task); if (resultingProxies.size() == proxies.length) removeIndexTree(folder, new SubProgressMonitor(monitor, 1), task); else { final IFileStore history= folder.getChild(RefactoringHistoryService.NAME_HISTORY_FILE); if (history.fetchInfo(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)).exists()) { InputStream input= null; Document document= null; try { input= new BufferedInputStream(history.openInputStream(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL))); document= getCachedDocument(path, input); } catch (ParserConfigurationException | IOException | SAXException exception) { throw createCoreException(exception); } finally { if (input != null) { try { input.close(); } catch (IOException exception) { // Do nothing } } } final Set<Node> removedNodes= new HashSet<>(proxies.length); final NodeList list= document.getElementsByTagName(IRefactoringSerializationConstants.ELEMENT_REFACTORING); final int length= list.getLength(); for (int offset= 0; offset < length; offset++) { final Node node= list.item(offset); final NamedNodeMap attributes= node.getAttributes(); if (attributes != null) { final Node item= attributes.getNamedItem(IRefactoringSerializationConstants.ATTRIBUTE_STAMP); if (item != null) { final String value= item.getNodeValue(); if (value != null) { for (RefactoringDescriptorProxy proxy : proxies) { final long stamp= proxy.getTimeStamp(); if (value.equals(String.valueOf(stamp))) { resultingProxies.remove(new DefaultRefactoringDescriptorProxy(proxy.getDescription(), proxy.getProject(), stamp)); removedNodes.add(node); } } } } } } for (final Iterator<Node> iterator= removedNodes.iterator(); iterator.hasNext();) { final Node node= iterator.next(); node.getParentNode().removeChild(node); } try { writeIndexEntry(index, resultingProxies.toArray(new RefactoringDescriptorProxy[resultingProxies.size()]), EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), task); writeHistoryEntry(history, document, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), task); } catch (IOException exception) { throw createCoreException(exception); } } } } } finally { monitor.done(); } }
Removes refactoring descriptors from the managed history.
Params:
  • proxies – the refactoring descriptors
  • monitor – the progress monitor to use
  • task – the task label to use
Throws:
/** * Removes refactoring descriptors from the managed history. * * @param proxies * the refactoring descriptors * @param monitor * the progress monitor to use * @param task * the task label to use * @throws CoreException * if an error occurs */
void removeRefactoringDescriptors(final RefactoringDescriptorProxy[] proxies, final IProgressMonitor monitor, final String task) throws CoreException { try { final Map<IPath, Collection<RefactoringDescriptorProxy>> paths= new HashMap<>(); monitor.beginTask(task, proxies.length + 300); for (RefactoringDescriptorProxy proxy : proxies) { final IPath path= stampToPath(proxy.getTimeStamp()); Collection<RefactoringDescriptorProxy> collection= paths.get(path); if (collection == null) { collection= new ArrayList<>(64); paths.put(path, collection); } collection.add(proxy); } final IProgressMonitor subMonitor= new SubProgressMonitor(monitor, 300); try { final Set<Entry<IPath, Collection<RefactoringDescriptorProxy>>> entries= paths.entrySet(); subMonitor.beginTask(task, entries.size()); for (final Iterator<Entry<IPath, Collection<RefactoringDescriptorProxy>>> iterator= entries.iterator(); iterator.hasNext();) { final Entry<IPath, Collection<RefactoringDescriptorProxy>> entry= iterator.next(); final Collection<RefactoringDescriptorProxy> collection= entry.getValue(); removeRefactoringDescriptors(collection.toArray(new RefactoringDescriptorProxy[collection.size()]), entry.getKey(), new SubProgressMonitor(subMonitor, 1), task); } } finally { subMonitor.done(); } } finally { monitor.done(); } }
Requests the resolved refactoring descriptor associated with the given proxy.
Params:
  • proxy – the refactoring descriptor proxy
  • monitor – the progress monitor to use
Returns:the associated refactoring descriptor, or null
/** * Requests the resolved refactoring descriptor associated with the given * proxy. * * @param proxy * the refactoring descriptor proxy * @param monitor * the progress monitor to use * @return the associated refactoring descriptor, or <code>null</code> */
RefactoringDescriptor requestDescriptor(final RefactoringDescriptorProxy proxy, final IProgressMonitor monitor) { try { monitor.beginTask(RefactoringCoreMessages.RefactoringHistoryService_resolving_information, 2); final long stamp= proxy.getTimeStamp(); if (stamp >= 0) { InputStream input= null; try { final IFileStore folder= fHistoryStore.getFileStore(stampToPath(stamp)); final IFileStore file= folder.getChild(RefactoringHistoryService.NAME_HISTORY_FILE); if (file.fetchInfo(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)).exists()) { input= new BufferedInputStream(file.openInputStream(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL))); final RefactoringSessionDescriptor descriptor= getCachedSession(file, fProjectName, input); if (descriptor != null) { final RefactoringDescriptor[] descriptors= descriptor.getRefactorings(); for (RefactoringDescriptor refactoringDescriptor : descriptors) { if (refactoringDescriptor.getTimeStamp() == stamp) { return refactoringDescriptor; } } } } } catch (CoreException exception) { RefactoringCorePlugin.log(exception); } finally { try { if (input != null) input.close(); } catch (IOException exception) { RefactoringCorePlugin.log(exception); } } } } finally { monitor.done(); } return null; }
Sets the comment of the specified refactoring.
Params:
  • proxy – the refactoring descriptor proxy
  • comment – the comment
  • monitor – the progress monitor to use
Throws:
/** * Sets the comment of the specified refactoring. * * @param proxy * the refactoring descriptor proxy * @param comment * the comment * @param monitor * the progress monitor to use * @throws CoreException * if an error occurs while setting the comment */
void setComment(final RefactoringDescriptorProxy proxy, final String comment, final IProgressMonitor monitor) throws CoreException { try { monitor.beginTask(RefactoringCoreMessages.RefactoringHistoryService_updating_history, 100); final long stamp= proxy.getTimeStamp(); if (stamp >= 0) { final IPath path= stampToPath(stamp); final IFileStore folder= fHistoryStore.getFileStore(path); final IFileStore history= folder.getChild(RefactoringHistoryService.NAME_HISTORY_FILE); if (history.fetchInfo(EFS.NONE, new SubProgressMonitor(monitor, 20, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)).exists()) { InputStream input= null; try { input= new BufferedInputStream(history.openInputStream(EFS.NONE, new SubProgressMonitor(monitor, 40, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL))); final Document document= getCachedDocument(path, input); try { input.close(); input= null; } catch (IOException exception) { // Do nothing } final String time= String.valueOf(stamp); final NodeList list= document.getElementsByTagName(IRefactoringSerializationConstants.ELEMENT_REFACTORING); for (int index= 0; index < list.getLength(); index++) { final Element element= (Element) list.item(index); if (time.equals(element.getAttribute(IRefactoringSerializationConstants.ATTRIBUTE_STAMP))) { element.setAttribute(IRefactoringSerializationConstants.ATTRIBUTE_COMMENT, comment); break; } } writeHistoryEntry(history, document, new SubProgressMonitor(monitor, 40, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL), RefactoringCoreMessages.RefactoringHistoryService_updating_history); } catch (ParserConfigurationException | IOException | SAXException exception) { throw createCoreException(exception); } finally { if (input != null) { try { input.close(); } catch (IOException exception) { // Do nothing } } } } } } finally { monitor.done(); } }
Writes the specified document node into the refactoring history.
Params:
  • file – the refactoring history file
  • document – the document representing the history entry
  • monitor – the progress monitor to use
  • task – the task label
Throws:
  • CoreException – if an error occurs while adding the history entry
/** * Writes the specified document node into the refactoring history. * * @param file * the refactoring history file * @param document * the document representing the history entry * @param monitor * the progress monitor to use * @param task * the task label * @throws CoreException * if an error occurs while adding the history entry */
private void writeHistoryEntry(final IFileStore file, final Document document, final IProgressMonitor monitor, final String task) throws CoreException { OutputStream output= null; try { monitor.beginTask(task, 2); file.getParent().mkdir(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); output= new BufferedOutputStream(file.openOutputStream(EFS.NONE, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL))); writeNode(output, document); } finally { fCachedDocument= null; fCachedPath= null; fCachedDescriptor= null; fCachedStore= null; if (output != null) { try { output.close(); } catch (IOException exception) { // Do nothing } } monitor.done(); } } }