blob: 2a31eba8244f3635da3e9d141a99795d042df0c3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Jens Lukowski/Innoopract - initial renaming/restructuring
*
*******************************************************************************/
package org.eclipse.wst.sse.ui.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.UnsupportedCharsetException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ContainerGenerator;
import org.eclipse.ui.editors.text.FileDocumentProvider;
import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel;
import org.eclipse.wst.sse.core.IFactoryRegistry;
import org.eclipse.wst.sse.core.IModelLifecycleListener;
import org.eclipse.wst.sse.core.IModelManager;
import org.eclipse.wst.sse.core.IModelStateListenerExtended;
import org.eclipse.wst.sse.core.IStructuredModel;
import org.eclipse.wst.sse.core.ModelLifecycleEvent;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.exceptions.SourceEditingRuntimeException;
import org.eclipse.wst.sse.core.internal.encoding.CodedReaderCreator;
import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento;
import org.eclipse.wst.sse.core.internal.encoding.EncodingRule;
import org.eclipse.wst.sse.core.internal.exceptions.CharConversionErrorWithDetail;
import org.eclipse.wst.sse.core.internal.exceptions.MalformedInputExceptionWithDetail;
import org.eclipse.wst.sse.core.internal.exceptions.MalformedOutputExceptionWithDetail;
import org.eclipse.wst.sse.core.internal.exceptions.UnsupportedCharsetExceptionWithDetail;
import org.eclipse.wst.sse.core.text.IStructuredDocument;
import org.eclipse.wst.sse.core.undo.IStructuredTextUndoManager;
import org.eclipse.wst.sse.ui.StructuredResourceMarkerAnnotationModel;
import org.eclipse.wst.sse.ui.registry.AdapterFactoryProvider;
import org.eclipse.wst.sse.ui.registry.AdapterFactoryRegistry;
import org.eclipse.wst.sse.ui.registry.AdapterFactoryRegistryImpl;
import org.eclipse.wst.sse.ui.util.Assert;
/**
* This version of an IDocumentProvider is the editor side counter part to
* IModelManager. It is responsible for providing the structuredModel, which
* is actually deferred to the IModelManager. But other, non-deffered
* responsibilities include providing editing related models, such as
* annotation models, undo command stack, etc.
*
* @deprecated - will be removed in M4
*/
public class FileModelProvider extends FileDocumentProvider implements IModelProvider {
// Ensure that the ElementInfo metadata is correct for the model
protected class InternalModelStateListener implements IModelStateListenerExtended {
public void modelAboutToBeChanged(IStructuredModel model) {
if (debugModelStatelistener) {
System.out.println("FileModelProvider: modelAboutToBeChanged: " + model.getId());
}
}
public void modelAboutToBeReinitialized(IStructuredModel model) {
if (debugModelStatelistener) {
System.out.println("FileModelProvider: modelAboutToBeReinitialized: " + model.getId());
}
}
public void modelChanged(IStructuredModel model) {
if (debugModelStatelistener) {
System.out.println("FileModelProvider: modelChanged: " + model.getId());
}
}
public void modelDirtyStateChanged(IStructuredModel model, boolean isDirty) {
if (debugModelStatelistener) {
System.out.println("FileModelProvider: modelDirtyStateChanged:(" + isDirty + ") " + model.getId());
}
// synchronize the saveability with the dirty flag
IFileEditorInput editorInput = (IFileEditorInput) getInputFor(model);
if (editorInput != null) {
FileInfo info = (FileInfo) getElementInfo(editorInput);
if (info != null) {
info.fCanBeSaved = isDirty;
if (!isDirty) {
addUnchangedElementListeners(editorInput, info);
// This timestamp udpate is required until
// ModelLifecycleEvent.MODEL_SAVED are received in the
// ModelInfo
info.fModificationStamp = computeModificationStamp(editorInput.getFile());
}
fireElementDirtyStateChanged(editorInput, isDirty);
} else {
throw new SourceEditingRuntimeException(new IllegalArgumentException("no corresponding element info found for " + model.getId()), "leftover model state listener in FileModelProvider"); //$NON-NLS-2$ //$NON-NLS-1$
}
}
fireModelDirtyStateChanged(model, isDirty);
}
public void modelReinitialized(IStructuredModel model) {
if (debugModelStatelistener) {
System.out.println("FileModelProvider: modelReinitialized " + model.getId());
}
reinitializeFactories(model);
}
public void modelResourceDeleted(IStructuredModel model) {
if (debugModelStatelistener) {
System.out.println("FileModelProvider: modelResourceDeleted " + model.getId());
}
fireModelDeleted(model);
}
public void modelResourceMoved(IStructuredModel originalmodel, IStructuredModel movedmodel) {
if (debugModelStatelistener) {
System.out.println("FileModelProvider: modelResourceMoved " + originalmodel.getBaseLocation() + " --> " + movedmodel.getBaseLocation());
}
fireModelMoved(originalmodel, movedmodel);
}
}
/**
* Collection of info that goes with a model. Implements
* IModelLifecycleListener so that there's a separate instance per
* IEditorInput
*/
protected class ModelInfo implements IModelLifecycleListener {
public IEditorInput fElement;
public FileSynchronizer fFileSynchronizer;
public boolean fShouldReleaseOnInfoDispose;
public IStructuredModel fStructuredModel;
public ModelInfo(IStructuredModel structuredModel, IEditorInput element, boolean selfCreated) {
fElement = element;
fStructuredModel = structuredModel;
fShouldReleaseOnInfoDispose = selfCreated;
fFileSynchronizer = createModelSynchronizer(element, structuredModel);
}
public void processPostModelEvent(ModelLifecycleEvent event) {
if (debugLifecyclelistener) {
System.out.println("FileModelProvider: " + event.getModel().getId() + " " + event.toString());
}
if (event.getType() == ModelLifecycleEvent.MODEL_SAVED) {
// update the recorded timestamps in the ElementInfo
FileInfo info = (FileInfo) getElementInfo(fElement);
if (info != null) {
info.fModificationStamp = computeModificationStamp(((IFileEditorInput) fElement).getFile());
} else {
throw new SourceEditingRuntimeException(new IllegalArgumentException("no corresponding element info found for " + event.getModel().getId()), "leftover model lifecycle listener"); //$NON-NLS-2$ //$NON-NLS-1$
}
}
}
public void processPreModelEvent(ModelLifecycleEvent event) {
if (debugLifecyclelistener) {
System.out.println("FileModelProvider: " + event.getModel().getId() + " " + event.toString());
}
}
}
static final boolean debugLifecyclelistener = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.ui/filemodelprovider/lifecyclelistener"));
static final boolean debugModelStatelistener = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.ui/filemodelprovider/modelstatelistener"));
private static final boolean debugOperations = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.ui/filemodelprovider/operations"));
private static FileModelProvider fInstance = null;
private static IModelManager fModelManager;
/** NLS strings */
// private static final String UNSUPPORTED_ENCODING_WARNING =
// ResourceHandler.getString("1This_encoding({0})_is_not__WARN_");
// //$NON-NLS-1$
//$NON-NLS-1$ = "This encoding({0}) is not supported. The default encoding will be used instead."
// CSSFileModelProvider will use this.
protected static final String UNSUPPORTED_ENCODING_WARNING_TITLE = SSEUIPlugin.getResourceString("%Encoding_warning_UI_"); //$NON-NLS-1$ = "Encoding warning"
public synchronized static FileModelProvider getInstance() {
if (fInstance == null)
fInstance = new FileModelProvider();
return fInstance;
}
/**
* Utility method also used in subclasses
*/
protected static IModelManager getModelManager() {
if (fModelManager == null) {
fModelManager = StructuredModelManager.getModelManager();
}
return fModelManager;
}
private Shell fActiveShell;
// workaround for save-as in C4
private Vector fChangingElements = new Vector(1);
private InternalModelStateListener fInternalModelStateListener;
/** IStructuredModel information of all connected elements */
private Map fModelInfoMap = new HashMap();
/**
* @deprecated - temporary flag to change the behavior of createDocument
* during revert/reset
*/
private boolean fReuseModelDocument = true;
protected FileModelProvider() {
fInternalModelStateListener = new InternalModelStateListener();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.texteditor.IDocumentProvider#aboutToChange(java.lang.Object)
*/
public void aboutToChange(Object element) {
super.aboutToChange(element);
// start recording for revert
IStructuredModel model = getModel(element);
if (model != null) {
fChangingElements.add(element);
IStructuredTextUndoManager undoMgr = model.getUndoManager();
undoMgr.beginRecording(this, 0, 0);
}
}
/**
* Register additional (viewer related) factories Note: this can be called
* twice, one for when model first created and loaded to editor. And
* possible later, if a model "reinit" takes place.
*/
protected void addProviderFactories(IStructuredModel structuredModel) {
// its an error to call with null argument
if (structuredModel == null)
return;
SSEUIPlugin plugin = ((SSEUIPlugin) Platform.getPlugin(SSEUIPlugin.ID));
AdapterFactoryRegistry adapterRegistry = plugin.getAdapterFactoryRegistry();
//Iterator adapterFactoryList =
// adapterRegistry.getAdapterFactories();
String contentTypeId = structuredModel.getContentTypeIdentifier();
Iterator adapterFactoryList = ((AdapterFactoryRegistryImpl) adapterRegistry).getAdapterFactories(contentTypeId);
IFactoryRegistry factoryRegistry = structuredModel.getFactoryRegistry();
Assert.isNotNull(factoryRegistry, "model in invalid state");
// And all those appropriate for this particular type of content
while (adapterFactoryList.hasNext()) {
try {
AdapterFactoryProvider provider = (AdapterFactoryProvider) adapterFactoryList.next();
// (pa) ContentType was already checked above
// this check is here for backwards compatability
// for those that still don't specify content type
if (provider.isFor(structuredModel.getModelHandler())) {
provider.addAdapterFactories(structuredModel);
}
} catch (Exception e) {
Logger.logException(e);
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.texteditor.IDocumentProvider#changed(java.lang.Object)
*/
public void changed(Object element) {
super.changed(element);
// end recording for revert
if (fChangingElements.contains(element)) {
fChangingElements.remove(element);
IStructuredTextUndoManager undoMgr = getModel(element).getUndoManager();
undoMgr.endRecording(this, 0, 0);
}
}
protected IAnnotationModel createAnnotationModel(Object element) throws CoreException {
if (debugOperations) {
if (element instanceof IStorageEditorInput)
System.out.println("FileModelProvider: createAnnotationModel for " + ((IStorageEditorInput) element).getStorage().getFullPath());
else
System.out.println("FileModelProvider: createAnnotationModel for " + element);
}
IAnnotationModel model = null;
if (element instanceof IEditorInput) {
IFile file = (IFile) ((IEditorInput) element).getAdapter(IFile.class);
model = new StructuredResourceMarkerAnnotationModel(file);
}
if (model == null)
model = super.createAnnotationModel(element);
return model;
}
/**
* @see org.eclipse.ui.texteditor.AbstractDocumentProvider#createDocument(java.lang.Object)
*/
protected IDocument createDocument(Object element) {
IDocument document = null;
if (debugOperations) {
if (element instanceof IFileEditorInput)
System.out.println("FileModelProvider: createDocument for " + ((IFileEditorInput) element).getFile().getFullPath());
else
System.out.println("FileModelProvider: createDocument for " + element);
}
if (element instanceof IEditorInput) {
// create a new IDocument for the element; should always reflect
// the contents of the resource
ModelInfo info = getModelInfoFor((IEditorInput) element);
if (info == null) {
throw new SourceEditingRuntimeException(new IllegalArgumentException("no corresponding model info found")); //$NON-NLS-1$
}
IStructuredModel model = info.fStructuredModel;
if (model != null) {
if (!fReuseModelDocument && element instanceof IFileEditorInput) {
Reader reader = null;
IStructuredDocument innerdocument = null;
try {
// update document from file contents
IFile iFile = ((IFileEditorInput) element).getFile();
CodedReaderCreator codedReaderCreator = new CodedReaderCreator(iFile);
reader = codedReaderCreator.getCodedReader();
innerdocument = model.getStructuredDocument();
int originalLengthToReplace = innerdocument.getLength();
//TODO_future: we could implement with sequential
// rewrite, if we don't
// pickup automatically from FileBuffer support, so
// not so
// much has to be pulled into memory (as an extra big
// string), but
// we need to carry that API through so that
// StructuredModel is not
// notified until done.
//innerdocument.startSequentialRewrite(true);
//innerdocument.replaceText(this, 0,
// innerdocument.getLength(), "");
StringBuffer stringBuffer = new StringBuffer();
int bufferSize = 2048;
char[] buffer = new char[bufferSize];
int nRead = 0;
boolean eof = false;
while (!eof) {
nRead = reader.read(buffer, 0, bufferSize);
if (nRead == -1) {
eof = true;
} else {
stringBuffer.append(buffer, 0, nRead);
//innerdocument.replaceText(this,
// innerdocument.getLength(), 0, new
// String(buffer, 0, nRead));
}
}
// ignore read-only settings if reverting whole
// document
innerdocument.replaceText(this, 0, originalLengthToReplace, stringBuffer.toString(), true);
model.setDirtyState(false);
} catch (CoreException e) {
Logger.logException(e);
} catch (IOException e) {
Logger.logException(e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
// would be highly unusual
Logger.logException(e1);
}
}
// if (innerdocument != null) {
// innerdocument.stopSequentialRewrite();
// }
}
}
if (document == null) {
document = model.getStructuredDocument();
}
}
}
return document;
}
/**
* Also create ModelInfo - extra resource synchronization classes should
* be stored within the ModelInfo
*/
protected ElementInfo createElementInfo(Object element) throws CoreException {
if (debugOperations) {
if (element instanceof IFileEditorInput)
System.out.println("FileModelProvider: createElementInfo for " + ((IFileEditorInput) element).getFile().getFullPath());
else
System.out.println("FileModelProvider: createElementInfo for " + element);
}
// create the corresponding ModelInfo if necessary
if (getModelInfoFor((IEditorInput) element) == null) {
createModelInfo((IEditorInput) element);
}
ElementInfo info = super.createElementInfo(element);
return info;
}
/**
* NOT API
*
* @param input
*/
public void createModelInfo(IEditorInput input) {
if (debugOperations) {
if (input instanceof IFileEditorInput)
System.out.println("FileModelProvider: createModelInfo for " + ((IFileEditorInput) input).getFile().getFullPath());
else
System.out.println("FileModelProvider: createModelInfo for " + input);
}
if (!(input instanceof IFileEditorInput)) {
throw new SourceEditingRuntimeException(new IllegalArgumentException("This model provider only supports IFileEditorInputs")); //$NON-NLS-1$
}
if (getModelInfoFor(input) == null) {
IStructuredModel structuredModel = selfCreateModel((IFileEditorInput) input);
createModelInfo(input, structuredModel, true);
}
}
/**
* NOT API - setModel(IStructuredModel, IEditorInput) should be used
* instead to force IEditorInput to IStructuredModel associations
*
* To be used when model is provided to us, ensures that when setInput is
* used on this input, the given model will be used.
*/
public void createModelInfo(IEditorInput input, IStructuredModel structuredModel, boolean releaseModelOnDisconnect) {
if (!(input instanceof IFileEditorInput)) {
throw new SourceEditingRuntimeException(new IllegalArgumentException("This model provider only supports IFileEditorInputs")); //$NON-NLS-1$
} else if (structuredModel == null) {
throw new SourceEditingRuntimeException(new IllegalArgumentException("No model loaded for input: " + input.getName())); //$NON-NLS-1$
}
// we have to make sure factories are added, whether we created or
// not.
if (getModelInfoFor(input) != null)
return;
// When a model is moved, it may be in the map for two IEditorInputs
// for
// a brief time. Ensure that model "setup" is only performed once.
ModelInfo existingModelInfo = getModelInfoFor(structuredModel);
if (existingModelInfo == null) {
addProviderFactories(structuredModel);
} else {
reinitializeFactories(structuredModel);
}
ModelInfo modelInfo = new ModelInfo(structuredModel, input, releaseModelOnDisconnect);
fModelInfoMap.put(input, modelInfo);
if (modelInfo.fFileSynchronizer != null) {
modelInfo.fFileSynchronizer.install();
}
modelInfo.fStructuredModel.addModelStateListener(fInternalModelStateListener);
modelInfo.fStructuredModel.addModelLifecycleListener(modelInfo);
// fix once approved
// we only resetSynchronizationStamp on model if its not set already
if (input.getAdapter(IFile.class) != null && modelInfo.fStructuredModel.getSynchronizationStamp() == IResource.NULL_STAMP) {
modelInfo.fStructuredModel.resetSynchronizationStamp((IResource) input.getAdapter(IFile.class));
}
}
protected FileSynchronizer createModelSynchronizer(IEditorInput input, IStructuredModel model) {
return null;
}
protected void disposeElementInfo(Object element, ElementInfo info) {
if (debugOperations) {
if (element instanceof IFileEditorInput)
System.out.println("FileModelProvider: disposeElementInfo for " + ((IFileEditorInput) element).getFile().getFullPath());
else
System.out.println("FileModelProvider: disposeElementInfo for " + element);
}
if (element instanceof IEditorInput) {
IEditorInput input = (IEditorInput) element;
ModelInfo modelInfo = getModelInfoFor(input);
if (modelInfo.fFileSynchronizer != null) {
modelInfo.fFileSynchronizer.uninstall();
}
disposeModelInfo(modelInfo);
}
super.disposeElementInfo(element, info);
}
/**
* disconnect from this model info
*
* @param info
*/
public void disposeModelInfo(ModelInfo info) {
info.fStructuredModel.removeModelLifecycleListener(info);
info.fStructuredModel.removeModelStateListener(fInternalModelStateListener);
if (info.fShouldReleaseOnInfoDispose) {
if (debugOperations) {
if (info.fElement instanceof IFileEditorInput)
System.out.println("FileModelProvider: disposeModelInfo and releaseFromEdit for " + ((IFileEditorInput) info.fElement).getFile().getFullPath());
else
System.out.println("FileModelProvider: disposeModelInfo and releaseFromEdit for " + info.fElement);
}
info.fStructuredModel.releaseFromEdit();
} else if (debugOperations) {
if (info.fElement instanceof IFileEditorInput)
System.out.println("FileModelProvider: disposeModelInfo without releaseFromEdit for " + ((IFileEditorInput) info.fElement).getFile().getFullPath());
else
System.out.println("FileModelProvider: disposeModelInfo without releaseFromEdit for " + info.fElement);
}
if (info.fFileSynchronizer != null) {
info.fFileSynchronizer.uninstall();
}
fModelInfoMap.remove(info.fElement);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.texteditor.AbstractDocumentProvider#doResetDocument(java.lang.Object,
* org.eclipse.core.runtime.IProgressMonitor)
*/
protected void doResetDocument(Object element, IProgressMonitor monitor) throws CoreException {
fReuseModelDocument = false;
super.doResetDocument(element, monitor);
fReuseModelDocument = true;
}
public void doSaveDocument(IProgressMonitor progressMonitor, Object element, IDocument document, boolean overwrite) throws CoreException {
//TODO: still need to "transfer" much of this logic to model level
boolean success = false;
Assert.isTrue(element instanceof IFileEditorInput);
IFileEditorInput input = (IFileEditorInput) element;
IStructuredModel model = getModel(getInputFor(document));
Assert.isNotNull(model);
boolean localDirtyState = model.isDirty();
IFile resource = input.getFile();
try {
if (!overwrite) {
checkSynchronizationState(((FileInfo) getElementInfo(element)).fModificationStamp, resource);
}
// inform about the upcoming content change
fireElementStateChanging(element);
try {
model.save(resource); //, encodingname, null);
} catch (UnsupportedCharsetException exception) {
Shell shell = getActiveShell();
// FYI: made this indirect chain of calls to help get rid of
// our specific Exception, need to test
// that all is updated in right order, even when error.
EncodingMemento encodingMemento = model.getStructuredDocument().getEncodingMemento();
String foundEncoding = encodingMemento.getInvalidEncoding();
String defaultToUse = encodingMemento.getAppropriateDefault();
boolean tryDefault = openUnsupportEncodingForSave(foundEncoding, defaultToUse, resource.getName(), shell);
if (tryDefault) {
model.save(resource, EncodingRule.FORCE_DEFAULT);
} else {
// User has canceled Save. Keep view opened
progressMonitor.setCanceled(true);
return;
}
} catch (MalformedOutputExceptionWithDetail exception) {
Shell shell = getActiveShell();
boolean userOK = openUnconvertableCharactersWarningForSave(exception, shell); //resource.getName()
if (userOK) {
model.save(resource, EncodingRule.IGNORE_CONVERSION_ERROR);
} else {
// User has canceled Save. Keep view opened
progressMonitor.setCanceled(true);
return;
}
} catch (CharConversionErrorWithDetail exception) {
Shell shell = getActiveShell();
boolean userOK = openUnconvertableCharactersWarningForSave(exception, shell); //resource.getName()
if (userOK) {
model.save(resource, EncodingRule.IGNORE_CONVERSION_ERROR);
} else {
// User has canceled Save. Keep view opened
progressMonitor.setCanceled(true);
//outStream.close();
//outStream = null;
return;
}
}
if (!resource.exists()) {
progressMonitor.beginTask(SSEUIPlugin.getResourceString("%FileDocumentProvider.task.saving"), 2000); //$NON-NLS-1$
ContainerGenerator generator = new ContainerGenerator(resource.getParent().getFullPath());
generator.generateContainer(new SubProgressMonitor(progressMonitor, 1000));
}
// if we get to here without an exception... success!
success = true;
model.setDirtyState(false);
model.resetSynchronizationStamp(resource);
// update markers
if (getAnnotationModel(element) instanceof AbstractMarkerAnnotationModel) {
((AbstractMarkerAnnotationModel) getAnnotationModel(element)).updateMarkers(document);
}
// reset the modification stamp record so we know we don't try to
// reload on following resource change notifications
FileInfo info = (FileInfo) getElementInfo(element);
if (info != null) {
info.fModificationStamp = computeModificationStamp(resource);
}
//For error handling test only!!!==========
//
//Uncomment the following line of code to simulate a
// FileNotFoundException.
//throw new FileNotFoundException();
//
//Uncomment the following line of code to simulate a
// UnsupportedEncodingException.
//throw new UnsupportedEncodingException();
//
//Uncomment the following line of code to simulate a IOException.
//throw new IOException();
//For error handling test only!!!==========
} catch (java.io.FileNotFoundException exception) {
/*
* The FileNotFoundException's message may not be very meaningful
* to user. Since the SourceEditingRuntimeException(exception,
* message) form will use just the detail message in the passed-in
* exception (instead of concatenating the detail message with the
* additional message), we are converting this exception into a
* more informative message. Same idea applies to the following
* catch blocks.
*/
progressMonitor.setCanceled(true);
// inform about failure
fireElementStateChangeFailed(element);
throw new SourceEditingRuntimeException(SSEUIPlugin.getResourceString("%Unable_to_save_the_documen_ERROR_")); //$NON-NLS-1$ = "Unable to save the document. Output file not found."
} catch (UnsupportedEncodingException exception) {
progressMonitor.setCanceled(true);
// inform about failure
fireElementStateChangeFailed(element);
throw new SourceEditingRuntimeException(SSEUIPlugin.getResourceString("%Unable_to_save_the_documen1_ERROR_")); //$NON-NLS-1$ = "Unable to save the document. Unsupported encoding."
} catch (java.io.IOException exception) {
progressMonitor.setCanceled(true);
// inform about failure
fireElementStateChangeFailed(element);
throw new SourceEditingRuntimeException(SSEUIPlugin.getResourceString("%Unable_to_save_the_documen2_ERROR_")); //$NON-NLS-1$
//$NON-NLS-1$ = "Unable to save the document. An I/O error occurred while saving the document."
} catch (OperationCanceledException exception) {
Logger.log(Logger.INFO, "Save Operation Canceled at user's request"); //$NON-NLS-1$
} finally {
if (!success) {
model.setDirtyState(localDirtyState);
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.texteditor.IDocumentProviderExtension#synchronize(java.lang.Object)
*/
public void doSynchronize(Object element, IProgressMonitor monitor) throws CoreException {
super.doSynchronize(element, monitor);
if (element instanceof IFileEditorInput) {
IFileEditorInput input = (IFileEditorInput) element;
getModel(input).resetSynchronizationStamp(input.getFile());
}
}
protected void fireModelContentAboutToBeReplaced(IStructuredModel model) {
}
protected void fireModelContentReplaced(IStructuredModel model) {
}
protected void fireModelDeleted(IStructuredModel model) {
}
protected void fireModelDirtyStateChanged(IStructuredModel model, final boolean isDirty) {
}
protected void fireModelMoved(IStructuredModel originalModel, IStructuredModel movedModel) {
}
protected Shell getActiveShell() {
Shell shell = null;
// Looks like Display tells me what is the current active Shell
// so that it seems not to ask EditorPart to give me a Shell.
// Same technique in used by
// Infopop(org.eclipse.help.internal.ui.ContextHelpDialog(Object [],
// int, int))
// looks like there's not always an active shell, at least
// during debugging the save operation with invalid encoding
if (fActiveShell != null) {
shell = fActiveShell;
} else {
Display dsp = getDisplay();
shell = dsp.getActiveShell();
}
return shell;
}
private Display getDisplay() {
return PlatformUI.getWorkbench().getDisplay();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.editors.text.IStorageDocumentProvider#getEncoding(java.lang.Object)
*/
public String getEncoding(Object element) {
EncodingMemento em = null;
IStructuredModel model = getModel((IEditorInput) element);
// model can be null if opened on non-expected file
if (model != null) {
em = model.getStructuredDocument().getEncodingMemento();
}
// DefaultEncodingSupport uses IANA names
if (em != null)
return em.getDetectedCharsetName();
return super.getEncoding(element);
}
protected IEditorInput getInputFor(IDocument document) {
IStructuredModel model = getModelManager().getExistingModelForRead(document);
IEditorInput input = getInputFor(model);
model.releaseFromRead();
return input;
}
protected IEditorInput getInputFor(IStructuredModel structuredModel) {
IEditorInput result = null;
ModelInfo info = getModelInfoFor(structuredModel);
if (info != null)
result = info.fElement;
return result;
}
public IStructuredModel getModel(IEditorInput element) {
IStructuredModel result = null;
ModelInfo info = getModelInfoFor(element);
if (info != null) {
result = info.fStructuredModel;
}
return result;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.wst.sse.ui.IModelProvider#getModel(java.lang.Object)
*/
public IStructuredModel getModel(Object element) {
Assert.isTrue(element instanceof IFileEditorInput);
return getModel((IFileEditorInput) element);
}
protected ModelInfo getModelInfoFor(IEditorInput element) {
ModelInfo result = (ModelInfo) fModelInfoMap.get(element);
return result;
}
protected ModelInfo getModelInfoFor(IStructuredModel structuredModel) {
ModelInfo result = null;
if (structuredModel != null) {
ModelInfo[] modelInfos = (ModelInfo[]) fModelInfoMap.values().toArray(new ModelInfo[0]);
for (int i = 0; i < modelInfos.length; i++) {
ModelInfo info = modelInfos[i];
if (structuredModel.equals(info.fStructuredModel)) {
result = info;
break;
}
}
}
return result;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.editors.text.FileDocumentProvider#getPersistedEncoding(java.lang.Object)
*/
protected String getPersistedEncoding(Object element) {
EncodingMemento em = null;
IStructuredModel model = getModel((IEditorInput) element);
if (model != null) {
em = model.getStructuredDocument().getEncodingMemento();
}
// DefaultEncodingSupport uses IANA names
if (em != null) {
return em.getDetectedCharsetName();
} else {
// error condition
return ""; //$NON-NLS-1$
}
}
/**
* Defines the standard procedure to handle CoreExceptions.
*
* @param exception
* the exception to be logged
* @param message
* the message to be logged
*/
protected void handleCoreException(CoreException exception, String message) {
Logger.logException(message, exception);
}
/**
* Updates the element info and Document contents to a change of the file
* content and sends out appropriate notifications.
*
* @param fileEditorInput
* the input of a text editor
*/
protected void handleElementContentChanged(IFileEditorInput fileEditorInput) {
FileInfo info = (FileInfo) getElementInfo(fileEditorInput);
if (info == null)
return;
String oldContents = getModel(fileEditorInput).getStructuredDocument().get();
try {
refreshFile(fileEditorInput.getFile());
} catch (CoreException x) {
handleCoreException(x, "FileDocumentProvider.handleElementContentChanged"); //$NON-NLS-1$
}
// set the new content and fire content related events
fireElementContentAboutToBeReplaced(fileEditorInput);
removeUnchangedElementListeners(fileEditorInput, info);
// direct superclass also removes but never adds?
info.fDocument.removeDocumentListener(info);
boolean reloaded = false;
InputStream inStream = null;
try {
inStream = fileEditorInput.getFile().getContents(true);
getModel(fileEditorInput).reload(inStream);
reloaded = true;
} catch (IOException e) {
String message = MessageFormat.format(SSEUIPlugin.getResourceString("%FileModelProvider.0"), new String[]{fileEditorInput.getName()}); //$NON-NLS-1$
info.fStatus = new Status(IStatus.ERROR, SSEUIPlugin.ID, IStatus.ERROR, message, e);
} catch (CoreException e) {
info.fStatus = e.getStatus();
} finally {
if (inStream != null) {
try {
inStream.close();
} catch (IOException e1) {
Logger.logException(e1);
}
}
}
info.fDocument = getModel(fileEditorInput).getStructuredDocument();
info.fCanBeSaved = false;
info.fModificationStamp = computeModificationStamp(fileEditorInput.getFile());
if (reloaded) {
info.fStatus = null;
}
addUnchangedElementListeners(fileEditorInput, info);
fireElementContentReplaced(fileEditorInput);
if (!reloaded) {
info.fDocument.set(oldContents);
}
}
/**
* @see org.eclipse.ui.editors.text.FileDocumentProvider#isModifiable(java.lang.Object)
*/
public boolean isModifiable(Object element) {
return super.isModifiable(element); //!isReadOnly(element);
}
/**
* @see org.eclipse.ui.texteditor.IDocumentProvider#mustSaveDocument(java.lang.Object)
* <br>
*
* The rule is that if it is sharedForEdit, then it never "must be saved",
* but if its not sharedForEdit, then it depends on its dirty state.
*/
public boolean mustSaveDocument(Object element) {
boolean result = false;
if (getModel((IEditorInput) element) != null) {
if (!getModel((IEditorInput) element).isSharedForEdit()) {
result = getModel((IEditorInput) element).isDirty();
}
}
return result;
}
private boolean openUnconvertableCharactersWarningForSave(CharConversionErrorWithDetail outputException, Shell topshell) {
// open message dialog
final String title = UNSUPPORTED_ENCODING_WARNING_TITLE;
String userMessage = SSEUIPlugin.getResourceString("%cannot_convert_some_characters2"); //$NON-NLS-1$
MessageFormat form = new MessageFormat(userMessage);
Object[] args = {outputException.getCharsetName()};
final String msg = form.format(args);
Shell shell = getActiveShell();
MessageDialog warning = new MessageDialog(shell, title, null, msg, MessageDialog.QUESTION, new String[]{IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL}, 0);
if (warning.open() == 0)
return true;
return false;
}
protected boolean openUnconvertableCharactersWarningForSave(MalformedOutputExceptionWithDetail outputException, Shell topshell) {
// open message dialog
final String title = UNSUPPORTED_ENCODING_WARNING_TITLE;
String userMessage = SSEUIPlugin.getResourceString("%cannot_convert_some_characters"); //$NON-NLS-1$
MessageFormat form = new MessageFormat(userMessage);
Object[] args = {outputException.getAttemptedIANAEncoding(), Integer.toString(outputException.getCharPosition())};
final String msg = form.format(args);
Shell shell = getActiveShell();
MessageDialog warning = new MessageDialog(shell, title, null, msg, MessageDialog.QUESTION, new String[]{IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL}, 0);
if (warning.open() == 0)
return true;
return false;
}
protected void openUndecodableCharacterError(MalformedInputExceptionWithDetail e) {
// checked and coordinated among all editors.
String title = SSEUIPlugin.getResourceString("%Error_opening_file_UI_"); //$NON-NLS-1$ = "Error opening file"
String msg = e.toString();
// if the exception char position < 0, perhaps we exceeded the max
// buffer when detecting pos of error
// if that is the case, display a different error message
IStatus status;
if ((e.getCharPosition() < 0) && (e.isExceededMax()))
status = new Status(IStatus.ERROR, SSEUIPlugin.ID, 0, SSEUIPlugin.getResourceString("%8concat_ERROR_", (new Object[]{Integer.toString(e.getMaxBuffer()), e.getAttemptedIANAEncoding()})), e); //$NON-NLS-1$
else
status = new Status(IStatus.ERROR, SSEUIPlugin.ID, 0, SSEUIPlugin.getResourceString("%7concat_ERROR_", (new Object[]{(Integer.toString(e.getCharPosition())), e.getAttemptedIANAEncoding()})), e); //$NON-NLS-1$
//$NON-NLS-1$ = "Could not be decoded character (at position {0}) according to the encoding parameter {1}"
ErrorDialog.openError(getActiveShell(), title, msg, status);
}
protected void openUnsupportedEncodingWarningForLoad(String encoding, String defaultToUse) {
// open message dialog
final String title = UNSUPPORTED_ENCODING_WARNING_TITLE;
MessageFormat form = new MessageFormat(SSEUIPlugin.getResourceString("%This_encoding_({0})_is_not_supported._The_default_encoding_({1})_will_be_used_instead._1")); //$NON-NLS-1$
Object[] args = {encoding, defaultToUse};
final String msg = form.format(args);
Shell shell = getActiveShell();
MessageDialog warning = new MessageDialog(shell, title, null, msg, MessageDialog.WARNING, new String[]{IDialogConstants.OK_LABEL}, 0);
warning.open();
}
protected boolean openUnsupportEncodingForSave(String foundEncoding, String defaultEncodingToUse, String dialogTitle, Shell topshell) {
if (topshell == null)
return true; // if no topshell, return true;
// open message dialog
// MessageFormat form = new
// MessageFormat(ResourceHandler.getString("This_encoding({0})_is_not__WARN_"));
// //$NON-NLS-1$ = "This encoding({0}) is not supported. Continue ?"
MessageFormat form = new MessageFormat(SSEUIPlugin.getResourceString("%This_encoding_({0})_is_not_supported._Continue_the_save_using_the_default_({1})__2")); //$NON-NLS-1$
Object[] args = {foundEncoding, defaultEncodingToUse};
final String msg = form.format(args);
MessageDialog warning = new MessageDialog(topshell, dialogTitle, null, msg, MessageDialog.QUESTION, new String[]{IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL}, 0);
if (warning.open() == 0)
return true;
return false;
}
/**
* Register additional (viewer related) factories Note: this can be called
* twice, one for when model first created and loaded to editor. And
* possible later, if a model "reinit" takes place.
*/
protected void reinitializeFactories(IStructuredModel structuredModel) {
// its an error to call with null argument
if (structuredModel == null)
return;
SSEUIPlugin plugin = ((SSEUIPlugin) Platform.getPlugin(SSEUIPlugin.ID));
AdapterFactoryRegistry adapterRegistry = plugin.getAdapterFactoryRegistry();
Iterator adapterList = adapterRegistry.getAdapterFactories();
// And all those appropriate for this particular type of content
while (adapterList.hasNext()) {
try {
AdapterFactoryProvider provider = (AdapterFactoryProvider) adapterList.next();
if (provider.isFor(structuredModel.getModelHandler())) {
provider.reinitializeFactories(structuredModel);
}
} catch (Exception e) {
Logger.logException(e);
}
}
}
protected IStructuredModel selfCreateModel(IFileEditorInput input) {
// this method should be called only after is established the
// desired model is not in the active items list.
IStructuredModel structuredModel = getModelManager().getExistingModelForEdit(input.getFile());
if (structuredModel == null) {
try {
try {
structuredModel = getModelManager().getModelForEdit(input.getFile()); //
} catch (UnsupportedCharsetException exception) {
// TODO-future: user should be given a choice here to try
// other encodings other than 'default'
EncodingMemento encodingMemento = null;
if (exception instanceof UnsupportedCharsetExceptionWithDetail) {
UnsupportedCharsetExceptionWithDetail detailedException = (UnsupportedCharsetExceptionWithDetail) exception;
encodingMemento = detailedException.getEncodingMemento();
}
String foundEncoding = encodingMemento.getInvalidEncoding();
String defaultToUse = encodingMemento.getAppropriateDefault();
openUnsupportedEncodingWarningForLoad(foundEncoding, defaultToUse);
try {
structuredModel = getModelManager().getModelForEdit(input.getFile(), EncodingRule.FORCE_DEFAULT); //$NON-NLS-1$ //$NON-NLS-2$
} catch (MalformedInputExceptionWithDetail ex) { // this
openUndecodableCharacterError(ex);
structuredModel = null;
}
} catch (MalformedInputExceptionWithDetail e) {
openUndecodableCharacterError(e);
structuredModel = getModelManager().getModelForEdit(input.getFile(), EncodingRule.IGNORE_CONVERSION_ERROR);
}
} catch (CoreException e) {
throw new SourceEditingRuntimeException(e);
} catch (IOException e) {
throw new SourceEditingRuntimeException(e);
}
}
return structuredModel;
}
/**
* we should figure out how not to need this in future. I've found that
* during 'save', the active shell, obtainable from Display, is null, so
* this shell must be set by editor before requesting the save. We should
* check "legality" directly from editor.
*/
public void setActiveShell(Shell activeShell) {
fActiveShell = activeShell;
}
/**
* This method is intended for those uses where the model has already been
* obtained and provided by the client (with its own id, not necessarily
* the one we would create by default). The IEditorInput must still be
* connect()ed before regular IDocumentProvider APIs will function.
*/
public void setModel(IStructuredModel model, IEditorInput input) {
if (debugOperations) {
if (input instanceof IFileEditorInput)
System.out.println("FileModelProvider: setModel override used for " + ((IFileEditorInput) input).getFile().getFullPath());
else
System.out.println("FileModelProvider: setModel override used for " + input);
}
createModelInfo(input, model, false);
}
}