| /*=============================================================================# |
| # Copyright (c) 2009, 2017 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.nico.core.util; |
| |
| import java.io.BufferedWriter; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.EnumSet; |
| import java.util.List; |
| |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.variables.IDynamicVariable; |
| import org.eclipse.debug.core.IStreamListener; |
| import org.eclipse.debug.core.model.IStreamMonitor; |
| import org.eclipse.osgi.util.NLS; |
| |
| import org.eclipse.statet.jcommons.lang.Disposable; |
| |
| import org.eclipse.statet.ecommons.io.FileUtil; |
| import org.eclipse.statet.ecommons.variables.core.ILocationVariable; |
| import org.eclipse.statet.ecommons.variables.core.VariableText; |
| import org.eclipse.statet.ecommons.variables.core.WrappedDynamicVariable; |
| |
| import org.eclipse.statet.internal.nico.core.NicoCorePlugin; |
| import org.eclipse.statet.nico.core.NicoCore; |
| import org.eclipse.statet.nico.core.runtime.IConsoleService; |
| import org.eclipse.statet.nico.core.runtime.ITrack; |
| import org.eclipse.statet.nico.core.runtime.SubmitType; |
| import org.eclipse.statet.nico.core.runtime.ToolController; |
| import org.eclipse.statet.nico.core.runtime.ToolProcess; |
| import org.eclipse.statet.nico.core.runtime.ToolStreamMonitor; |
| import org.eclipse.statet.nico.core.runtime.ToolStreamProxy; |
| import org.eclipse.statet.nico.core.runtime.ToolWorkspace; |
| |
| |
| public class TrackWriter implements ITrack, IStreamListener, Disposable { |
| |
| |
| private static final String TRUNCATE_INFO = "[...] (truncated)\n\n"; |
| |
| public static String getTruncateInfo() { |
| return TRUNCATE_INFO; |
| } |
| |
| public static String resolveVariables(final String path, final ToolWorkspace workspace) throws CoreException { |
| final List<IDynamicVariable> variables = workspace.getStringVariables(); |
| final List<IDynamicVariable> checkedVariables= new ArrayList<>(variables.size()); |
| for (final IDynamicVariable variable : variables) { |
| if (variable instanceof ILocationVariable) { |
| checkedVariables.add(variable); |
| } |
| else { |
| checkedVariables.add(new WrappedDynamicVariable(variable) { |
| @Override |
| public String getValue(final String argument) throws CoreException { |
| return super.getValue(argument).replaceAll("\\\\|\\/|\\:", "-"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| }); |
| } |
| } |
| final VariableText text = new VariableText(path, checkedVariables, true); |
| text.performInitialStringSubstitution(true); |
| text.performFinalStringSubstitution(null); |
| return text.getText(); |
| } |
| |
| |
| private final ToolController fController; |
| |
| private final TrackingConfiguration fConfig; |
| |
| private IFileStore fStoreFile; |
| private Writer fOutputWriter; |
| |
| private IStreamListener fInputListener; |
| private IStreamListener fOutputListener; |
| |
| private int fTrumcateMax; |
| private int fTruncateCurrent; |
| |
| |
| public TrackWriter(final ToolController controller, final TrackingConfiguration config) { |
| if (controller == null || config == null) { |
| throw new NullPointerException(); |
| } |
| fController = controller; |
| fConfig = config; |
| } |
| |
| |
| public IStatus init(final IProgressMonitor monitor) |
| throws CoreException { |
| OutputStream outputStream = null; |
| try { |
| try { |
| fStoreFile = resolveTrackingPath(fConfig.getFilePath()); |
| } |
| catch (final CoreException e) { |
| throw new CoreException(new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1, "Failed to resolve path of the tracking file.", e)); |
| } |
| |
| if (fConfig.getId().equals(HistoryTrackingConfiguration.HISTORY_TRACKING_ID) |
| && ((HistoryTrackingConfiguration) fConfig).getLoadHistory() |
| && fStoreFile.fetchInfo().exists()) { |
| fController.getTool().getHistory().load(fStoreFile, fConfig.getFileEncoding(), false, monitor); |
| } |
| |
| outputStream = fStoreFile.openOutputStream(fConfig.getFileMode(), monitor); |
| if (fStoreFile.fetchInfo().getLength() <= 0L) { |
| FileUtil.prepareTextOutput(outputStream, fConfig.getFileEncoding()); |
| } |
| fOutputWriter = new BufferedWriter(new OutputStreamWriter(outputStream, fConfig.getFileEncoding())); |
| |
| final EnumSet<SubmitType> submitTypes = fConfig.getSubmitTypes(); |
| final ToolStreamProxy streams = fController.getStreams(); |
| if (fConfig.getTrackStreamInfo()) { |
| streams.getInfoStreamMonitor().addListener(this, submitTypes); |
| } |
| if (fConfig.getTrackStreamInput()) { |
| fInputListener = (fConfig.getTrackStreamInputHistoryOnly()) ? |
| new IStreamListener() { |
| @Override |
| public void streamAppended(final String text, final IStreamMonitor monitor) { |
| if ((((ToolStreamMonitor) monitor).getMeta() & IConsoleService.META_HISTORY_DONTADD) == 0) { |
| TrackWriter.this.streamAppendedNL(text); |
| } |
| } |
| } : |
| new IStreamListener() { |
| @Override |
| public void streamAppended(final String text, final IStreamMonitor monitor) { |
| TrackWriter.this.streamAppended(text, monitor); |
| } |
| }; |
| streams.getInputStreamMonitor().addListener(fInputListener, submitTypes); |
| } |
| if (fConfig.getTrackStreamOutput()) { |
| if (fConfig.getTrackStreamOutputTruncate()) { |
| fTrumcateMax = fConfig.getTrackStreamOutputTruncateLines(); |
| fOutputListener = new IStreamListener() { |
| @Override |
| public void streamAppended(final String text, final IStreamMonitor monitor) { |
| TrackWriter.this.streamAppendedTruncateOutput(text); |
| } |
| }; |
| } |
| else { |
| fOutputListener = this; |
| } |
| streams.getOutputStreamMonitor().addListener(fOutputListener, submitTypes); |
| streams.getErrorStreamMonitor().addListener(this, submitTypes); |
| streams.getSystemOutputMonitor().addListener(fOutputListener, submitTypes); |
| } |
| |
| if (fConfig.getPrependTimestamp()) { |
| final ToolProcess process = fController.getTool(); |
| final String comment = process.createTimestampComment(process.getConnectionTimestamp()); |
| try { |
| fOutputWriter.write(comment); |
| } |
| catch (final Exception e) { |
| onError(); |
| throw e; |
| } |
| } |
| |
| return Status.OK_STATUS; |
| } |
| catch (final Exception e) { |
| onError(); |
| if (outputStream != null) { |
| try { |
| outputStream.close(); |
| } catch (final IOException ignore) {} |
| } |
| return new Status(IStatus.WARNING, NicoCore.BUNDLE_ID, -1, NLS.bind("Could not initialize tracking ''{0}''.", fConfig.getName()), e); |
| } |
| } |
| |
| protected IFileStore resolveTrackingPath(String filePath) throws CoreException { |
| filePath = resolveVariables(filePath, fController.getWorkspaceData()); |
| return FileUtil.getFileStore(filePath); |
| } |
| |
| @Override |
| public void streamAppended(final String text, final IStreamMonitor monitor) { |
| fTruncateCurrent = 0; |
| try { |
| fOutputWriter.write(text); |
| } |
| catch (final IOException e) { |
| NicoCorePlugin.log(new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1, "An error occurred when writing to the tracking file. Tracking is stopped.", e)); |
| onError(); |
| } |
| } |
| |
| private void streamAppendedTruncateOutput(String text) { |
| if (fTruncateCurrent == Integer.MAX_VALUE) { |
| return; |
| } |
| String text2 = null; |
| if (fTruncateCurrent > fTrumcateMax) { |
| fTruncateCurrent = Integer.MAX_VALUE; |
| text = TRUNCATE_INFO; |
| } |
| else { |
| int next = -1; |
| while ((next = text.indexOf('\n', next+1)) >= 0) { |
| if (++fTruncateCurrent > fTrumcateMax) { |
| if (text.length() != next+1) { |
| fTruncateCurrent = Integer.MAX_VALUE; |
| text = text.substring(0, next+1); |
| text2 = TRUNCATE_INFO; |
| } |
| break; |
| } |
| } |
| } |
| try { |
| fOutputWriter.write(text); |
| if (text2 != null) { |
| fOutputWriter.write(text2); |
| } |
| } |
| catch (final IOException e) { |
| NicoCorePlugin.log(new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1, "An error occurred when writing to the tracking file. Tracking is stopped.", e)); |
| onError(); |
| } |
| } |
| |
| private void streamAppendedNL(final String text) { |
| fTruncateCurrent = 0; |
| try { |
| fOutputWriter.write(text); |
| fOutputWriter.write('\n'); |
| } |
| catch (final IOException e) { |
| NicoCorePlugin.log(new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1, "An error occurred when writing to the tracking file. Tracking is stopped.", e)); |
| onError(); |
| } |
| } |
| |
| private void onError() { |
| final ToolStreamProxy streams = fController.getStreams(); |
| streams.getInfoStreamMonitor().removeListener(this); |
| if (fInputListener != null) { |
| streams.getInputStreamMonitor().removeListener(fInputListener); |
| } |
| if (fOutputListener != null) { |
| streams.getOutputStreamMonitor().removeListener(fOutputListener); |
| streams.getErrorStreamMonitor().removeListener(this); |
| } |
| if (fOutputWriter != null) { |
| try { |
| fOutputWriter.close(); |
| } |
| catch (final IOException ignore) {} |
| finally { |
| fOutputWriter = null; |
| } |
| } |
| dispose(); |
| } |
| |
| @Override |
| public void dispose() { |
| if (fOutputWriter != null) { |
| try { |
| fOutputWriter.close(); |
| } |
| catch (final IOException e) { |
| NicoCorePlugin.log(new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1, "An error occurred when closing the tracking file. Tracking is stopped.", e)); |
| } |
| finally { |
| fOutputWriter = null; |
| } |
| } |
| } |
| |
| @Override |
| public String getName() { |
| return fConfig.getName(); |
| } |
| |
| @Override |
| public void flush() { |
| final Writer writer = fOutputWriter; |
| if (writer != null) { |
| try { |
| writer.flush(); |
| } |
| catch (final IOException e) { |
| } |
| } |
| } |
| @Override |
| public IFileStore getFile() { |
| return fStoreFile; |
| } |
| |
| } |