| /******************************************************************************* |
| * Copyright (c) 2004, 2008 John Krasnay 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: |
| * John Krasnay - initial API and implementation |
| * Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325) |
| *******************************************************************************/ |
| package org.eclipse.wst.xml.vex.ui.internal.editor; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.URL; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.IResourceChangeListener; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| 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.Platform; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.text.IFindReplaceTarget; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.commands.ICommandService; |
| import org.eclipse.ui.contexts.IContextService; |
| import org.eclipse.ui.dialogs.SaveAsDialog; |
| import org.eclipse.ui.editors.text.ILocationProvider; |
| import org.eclipse.ui.part.EditorPart; |
| import org.eclipse.ui.part.FileEditorInput; |
| import org.eclipse.ui.services.IServiceLocator; |
| import org.eclipse.ui.services.ISourceProviderService; |
| import org.eclipse.ui.views.contentoutline.IContentOutlinePage; |
| import org.eclipse.ui.views.properties.IPropertySheetPage; |
| import org.eclipse.ui.views.properties.IPropertySource; |
| import org.eclipse.ui.views.properties.IPropertySourceProvider; |
| import org.eclipse.ui.views.properties.PropertySheetPage; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.eclipse.wst.xml.vex.core.internal.core.ListenerList; |
| import org.eclipse.wst.xml.vex.core.internal.dom.DOMDocumentReader; |
| import org.eclipse.wst.xml.vex.core.internal.dom.Document; |
| import org.eclipse.wst.xml.vex.core.internal.dom.DocumentWriter; |
| import org.eclipse.wst.xml.vex.core.internal.dom.Element; |
| import org.eclipse.wst.xml.vex.core.internal.provisional.dom.IWhitespacePolicy; |
| import org.eclipse.wst.xml.vex.core.internal.provisional.dom.IWhitespacePolicyFactory; |
| import org.eclipse.wst.xml.vex.core.internal.provisional.dom.I.VEXDocument; |
| import org.eclipse.wst.xml.vex.core.internal.provisional.dom.I.VEXElement; |
| import org.eclipse.wst.xml.vex.core.internal.provisional.dom.I.Validator; |
| import org.eclipse.wst.xml.vex.core.internal.validator.WTPVEXValidator; |
| import org.eclipse.wst.xml.vex.core.internal.widget.CssWhitespacePolicy; |
| import org.eclipse.wst.xml.vex.ui.internal.VexPlugin; |
| import org.eclipse.wst.xml.vex.ui.internal.config.ConfigEvent; |
| import org.eclipse.wst.xml.vex.ui.internal.config.DocumentType; |
| import org.eclipse.wst.xml.vex.ui.internal.config.IConfigListener; |
| import org.eclipse.wst.xml.vex.ui.internal.config.Style; |
| import org.eclipse.wst.xml.vex.ui.internal.handlers.ConvertElementHandler; |
| import org.eclipse.wst.xml.vex.ui.internal.handlers.RemoveTagHandler; |
| import org.eclipse.wst.xml.vex.ui.internal.outline.DocumentOutlinePage; |
| import org.eclipse.wst.xml.vex.ui.internal.property.ElementPropertySource; |
| import org.eclipse.wst.xml.vex.ui.internal.swt.VexWidget; |
| import org.osgi.service.prefs.BackingStoreException; |
| import org.osgi.service.prefs.Preferences; |
| import org.xml.sax.EntityResolver; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| |
| /** |
| * Editor for editing XML file using the VexWidget. |
| */ |
| public class VexEditor extends EditorPart { |
| |
| /** |
| * ID of this editor extension. |
| */ |
| public static final String ID = "org.eclipse.wst.xml.vex.ui.internal.editor.VexEditor"; //$NON-NLS-1$ |
| |
| /** |
| * Class constructor. |
| */ |
| public VexEditor() { |
| debugging = VexPlugin.getInstance().isDebugging() && "true".equalsIgnoreCase(Platform.getDebugOption(VexPlugin.ID + "/debug/layout")); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| /** |
| * Add a VexEditorListener to the notification list. |
| * |
| * @param listener |
| * VexEditorListener to be added. |
| */ |
| public void addVexEditorListener(final IVexEditorListener listener) { |
| vexEditorListeners.add(listener); |
| } |
| |
| @Override |
| public void dispose() { |
| super.dispose(); |
| |
| if (parentControl != null) |
| // createPartControl was called, so we must de-register from config |
| // events |
| VexPlugin.getInstance().getConfigurationRegistry().removeConfigListener(configListener); |
| |
| if (getEditorInput() instanceof IFileEditorInput) |
| ResourcesPlugin.getWorkspace().removeResourceChangeListener(resourceChangeListener); |
| |
| } |
| |
| @Override |
| public void doSave(final IProgressMonitor monitor) { |
| |
| final IEditorInput input = getEditorInput(); |
| OutputStream os = null; |
| try { |
| resourceChangeListener.setSaving(true); |
| final DocumentWriter writer = new DocumentWriter(); |
| writer.setWhitespacePolicy(new CssWhitespacePolicy(style.getStyleSheet())); |
| |
| if (input instanceof IFileEditorInput) { |
| final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| writer.write(doc, baos); |
| baos.close(); |
| final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); |
| ((IFileEditorInput) input).getFile().setContents(bais, false, false, monitor); |
| } else { |
| os = new FileOutputStream(((ILocationProvider) input).getPath(input).toFile()); |
| writer.write(doc, os); |
| } |
| |
| savedUndoDepth = vexWidget.getUndoDepth(); |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| |
| } catch (final Exception ex) { |
| monitor.setCanceled(true); |
| final String title = Messages.getString("VexEditor.errorSaving.title"); //$NON-NLS-1$ |
| final String message = MessageFormat.format(Messages.getString("VexEditor.errorSaving.message"), //$NON-NLS-1$ |
| new Object[] { input.getName(), ex.getMessage() }); |
| MessageDialog.openError(getEditorSite().getShell(), title, message); |
| VexPlugin.getInstance().log(IStatus.ERROR, message, ex); |
| } finally { |
| if (os != null) |
| try { |
| os.close(); |
| } catch (final IOException e) { |
| } |
| resourceChangeListener.setSaving(false); |
| } |
| } |
| |
| @Override |
| public void doSaveAs() { |
| final SaveAsDialog dlg = new SaveAsDialog(getSite().getShell()); |
| final int result = dlg.open(); |
| if (result == Window.OK) { |
| final IPath path = dlg.getResult(); |
| try { |
| resourceChangeListener.setSaving(true); |
| final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| final DocumentWriter writer = new DocumentWriter(); |
| writer.setWhitespacePolicy(new CssWhitespacePolicy(style.getStyleSheet())); |
| writer.write(doc, baos); |
| baos.close(); |
| |
| final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); |
| final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path); |
| file.create(bais, false, null); |
| |
| final IFileEditorInput input = new FileEditorInput(file); |
| setInput(input); |
| savedUndoDepth = vexWidget.getUndoDepth(); |
| |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| firePropertyChange(IEditorPart.PROP_INPUT); |
| firePropertyChange(IWorkbenchPart.PROP_TITLE); |
| |
| } catch (final Exception ex) { |
| final String title = Messages.getString("VexEditor.errorSaving.title"); //$NON-NLS-1$ |
| final String message = MessageFormat.format(Messages.getString("VexEditor.errorSaving.message"), //$NON-NLS-1$ |
| new Object[] { path, ex.getMessage() }); |
| MessageDialog.openError(getEditorSite().getShell(), title, message); |
| VexPlugin.getInstance().log(IStatus.ERROR, message, ex); |
| } finally { |
| resourceChangeListener.setSaving(false); |
| } |
| } |
| |
| } |
| |
| /** |
| * Return a reasonable style for the given doctype. |
| * |
| * @param publicId |
| * Public ID for which to return the style. |
| */ |
| public static Style getPreferredStyle(final String publicId) { |
| return VexPlugin.getInstance().getConfigurationRegistry().getStyle(publicId, getPreferredStyleId(publicId)); |
| } |
| |
| private static String getPreferredStyleId(final String publicId) { |
| final Preferences prefs = new InstanceScope().getNode(VexPlugin.ID); |
| final String preferredStyleId = prefs.get(getStylePreferenceKey(publicId), null); |
| return preferredStyleId; |
| } |
| |
| /** |
| * Returns the DocumentType associated with this editor. |
| */ |
| public DocumentType getDocumentType() { |
| return doctype; |
| } |
| |
| /** |
| * Returns the Style currently associated with the editor. May be null. |
| */ |
| public Style getStyle() { |
| return style; |
| } |
| |
| /** |
| * Returns the VexWidget that implements this editor. |
| */ |
| public VexWidget getVexWidget() { |
| return vexWidget; |
| } |
| |
| public void gotoMarker(final IMarker marker) { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| @Override |
| public void init(final IEditorSite site, final IEditorInput input) throws PartInitException { |
| |
| setSite(site); |
| setInput(input); |
| |
| getEditorSite().setSelectionProvider(selectionProvider); |
| getEditorSite().getSelectionProvider().addSelectionChangedListener(selectionChangedListener); |
| |
| if (input instanceof IFileEditorInput) |
| ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceChangeListener, IResourceChangeEvent.POST_CHANGE); |
| } |
| |
| protected void loadInput() { |
| |
| if (vexWidget != null) |
| vexEditorListeners.fireEvent("documentUnloaded", new VexEditorEvent(this)); //$NON-NLS-1$ |
| |
| loaded = false; |
| |
| final IEditorInput input = getEditorInput(); |
| |
| try { |
| final long start = System.currentTimeMillis(); |
| |
| IFile file = null; |
| |
| if (input instanceof IFileEditorInput) |
| file = ((IFileEditorInput) input).getFile(); |
| else { |
| final String msg = MessageFormat.format(Messages.getString("VexEditor.unknownInputClass"), //$NON-NLS-1$ |
| new Object[] { input.getClass() }); |
| showLabel(msg); |
| return; |
| } |
| |
| final DOMDocumentReader reader = new DOMDocumentReader(); |
| reader.setDebugging(debugging); |
| reader.setEntityResolver(entityResolver); |
| reader.setWhitespacePolicyFactory(wsFactory); |
| doctype = null; // must be null to set it to a new value via |
| // entityResolveras by following read(): |
| doc = reader.read(getDOMDocument(file)); |
| |
| if (debugging) { |
| final long end = System.currentTimeMillis(); |
| final String message = "Parsed document in " //$NON-NLS-1$ |
| + (end - start) + "ms"; //$NON-NLS-1$ |
| System.out.println(message); |
| } |
| |
| // this.doctype is set either by wsPolicyFactory or entityResolver |
| // this.style is set by wsPolicyFactory |
| // Otherwise, a PartInitException would have been thrown by now |
| |
| //IValidator validator = this.doctype.getValidator(); |
| final Validator validator = WTPVEXValidator.create(doctype.getResourceUrl()); |
| if (validator != null) { |
| doc.setValidator(validator); |
| if (debugging) { |
| final long end = System.currentTimeMillis(); |
| System.out.println("Got validator in " + (end - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| showVexWidget(); |
| |
| vexWidget.setDebugging(debugging); |
| vexWidget.setDocument(doc, style.getStyleSheet()); |
| |
| if (updateDoctypeDecl) { |
| doc.setPublicID(doctype.getPublicId()); |
| ((Document) doc).setSystemID(doctype.getSystemId()); |
| doSave(null); |
| } |
| |
| loaded = true; |
| savedUndoDepth = vexWidget.getUndoDepth(); |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| wasDirty = isDirty(); |
| |
| vexEditorListeners.fireEvent("documentLoaded", new VexEditorEvent(this)); //$NON-NLS-1$ |
| |
| } catch (final SAXParseException ex) { |
| |
| if (ex.getException() instanceof NoRegisteredDoctypeException) { |
| // TODO doc did not have document type and the user |
| // declined to select another one. Should fail silently. |
| String msg; |
| final NoRegisteredDoctypeException ex2 = (NoRegisteredDoctypeException) ex.getException(); |
| if (ex2.getPublicId() == null) |
| msg = Messages.getString("VexEditor.noDoctype"); //$NON-NLS-1$ |
| else |
| msg = MessageFormat.format(Messages.getString("VexEditor.unknownDoctype"), //$NON-NLS-1$ |
| new Object[] { ex2.getPublicId() }); |
| showLabel(msg); |
| } else if (ex.getException() instanceof NoStyleForDoctypeException) { |
| final String msg = MessageFormat.format(Messages.getString("VexEditor.noStyles"), //$NON-NLS-1$ |
| new Object[] { doctype.getPublicId() }); |
| showLabel(msg); |
| } else { |
| String file = ex.getSystemId(); |
| if (file == null) |
| file = input.getName(); |
| |
| final String msg = MessageFormat.format(Messages.getString("VexEditor.parseError"), //$NON-NLS-1$ |
| new Object[] { Integer.valueOf(ex.getLineNumber()), file, ex.getLocalizedMessage() }); |
| |
| showLabel(msg); |
| |
| VexPlugin.getInstance().log(IStatus.ERROR, msg, ex); |
| } |
| |
| } catch (final Exception ex) { |
| |
| final String msg = MessageFormat.format(Messages.getString("VexEditor.unexpectedError"), //$NON-NLS-1$ |
| new Object[] { input.getName() }); |
| |
| VexPlugin.getInstance().log(IStatus.ERROR, msg, ex); |
| |
| showLabel(msg); |
| } |
| } |
| |
| private IDOMDocument getDOMDocument(final IFile file) throws IOException, CoreException { |
| final IStructuredModel model = StructuredModelManager.getModelManager().getModelForRead(file); |
| IDOMDocument modelDocument = null; |
| try { |
| if (model instanceof IDOMModel) |
| modelDocument = ((IDOMModel) model).getDocument(); |
| } finally { |
| if (model != null) |
| model.releaseFromRead(); |
| } |
| return modelDocument; |
| } |
| |
| @Override |
| public boolean isDirty() { |
| if (vexWidget != null) |
| return savedUndoDepth != vexWidget.getUndoDepth(); |
| else |
| return false; |
| } |
| |
| /** |
| * Returns true if this editor has finished loading its document. |
| */ |
| public boolean isLoaded() { |
| return loaded; |
| } |
| |
| @Override |
| public boolean isSaveAsAllowed() { |
| return true; |
| } |
| |
| @Override |
| public void createPartControl(final Composite parent) { |
| |
| parentControl = parent; |
| |
| VexPlugin.getInstance().getConfigurationRegistry().addConfigListener(configListener); |
| if (VexPlugin.getInstance().getConfigurationRegistry().isLoaded()) |
| loadInput(); |
| else |
| showLabel(Messages.getString("VexEditor.loading")); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Remove a VexEditorListener from the notification list. |
| * |
| * @param listener |
| * VexEditorListener to be removed. |
| */ |
| public void removeVexEditorListener(final IVexEditorListener listener) { |
| vexEditorListeners.remove(listener); |
| } |
| |
| @Override |
| public void setFocus() { |
| if (vexWidget != null) { |
| vexWidget.setFocus(); |
| setStatus(getLocation()); |
| } |
| } |
| |
| @Override |
| protected void setInput(final IEditorInput input) { |
| super.setInput(input); |
| setPartName(input.getName()); |
| setContentDescription(input.getName()); |
| //this.setTitleToolTip(input.getToolTipText()); |
| } |
| |
| public void setStatus(final String text) { |
| // this.statusLabel.setText(text); |
| getEditorSite().getActionBars().getStatusLineManager().setMessage(text); |
| } |
| |
| /** |
| * Sets the style for this editor. |
| * |
| * @param style |
| * Style to use. |
| */ |
| public void setStyle(final Style style) { |
| this.style = style; |
| if (vexWidget != null) { |
| vexWidget.setStyleSheet(style.getStyleSheet()); |
| setPreferredStyleId(doc.getPublicID(), style.getUniqueId()); |
| } |
| } |
| |
| private static void setPreferredStyleId(final String publicId, final String styleId) { |
| final Preferences prefs = new InstanceScope().getNode(VexPlugin.ID); |
| final String key = getStylePreferenceKey(publicId); |
| prefs.put(key, styleId); |
| try { |
| prefs.flush(); |
| } catch (final BackingStoreException e) { |
| VexPlugin.getInstance().log(IStatus.ERROR, Messages.getString("VexEditor.errorSavingStylePreference"), e); //$NON-NLS-1$ |
| } |
| } |
| |
| // ========================================================= PRIVATE |
| |
| private final boolean debugging; |
| |
| private Composite parentControl; |
| private Label loadingLabel; |
| |
| private boolean loaded; |
| private DocumentType doctype; |
| private VEXDocument doc; |
| private Style style; |
| |
| private VexWidget vexWidget; |
| |
| private int savedUndoDepth; |
| private boolean wasDirty; |
| // private Label statusLabel; |
| |
| // This is true if the document's doctype decl is missing or unrecognized |
| // AND the user selected a new document type |
| // AND the user wants to always use the doctype for this document |
| private boolean updateDoctypeDecl; |
| |
| private final ListenerList<IVexEditorListener, VexEditorEvent> vexEditorListeners = new ListenerList<IVexEditorListener, VexEditorEvent>( |
| IVexEditorListener.class); |
| |
| private final SelectionProvider selectionProvider = new SelectionProvider(); |
| |
| /** |
| * Returns the preference key used to access the style ID for documents with |
| * the same public ID as the current document. |
| */ |
| private static String getStylePreferenceKey(final String publicId) { |
| return publicId + ".style"; //$NON-NLS-1$ |
| } |
| |
| private void showLabel(final String message) { |
| if (loadingLabel == null) { |
| if (vexWidget != null) { |
| vexWidget.dispose(); |
| vexWidget = null; |
| } |
| loadingLabel = new Label(parentControl, SWT.WRAP); |
| } |
| loadingLabel.setText(message); |
| parentControl.layout(true); |
| } |
| |
| private void showVexWidget() { |
| |
| if (vexWidget != null) |
| return; |
| |
| if (loadingLabel != null) { |
| loadingLabel.dispose(); |
| loadingLabel = null; |
| } |
| |
| final GridLayout layout = new GridLayout(); |
| layout.numColumns = 1; |
| layout.verticalSpacing = 0; |
| layout.marginHeight = 0; |
| layout.marginWidth = 0; |
| parentControl.setLayout(layout); |
| GridData gd; |
| |
| // StatusPanel statusPanel = new StatusPanel(this.parentControl); |
| |
| // Composite statusPanel = new Composite(this.parentControl, SWT.NONE); |
| // statusPanel.setLayout(new GridLayout()); |
| // gd = new GridData(); |
| // gd.grabExcessHorizontalSpace = true; |
| // gd.horizontalAlignment = GridData.FILL; |
| // statusPanel.setLayoutData(gd); |
| |
| // this.statusLabel = new Label(statusPanel, SWT.NONE); |
| // gd = new GridData(); |
| // gd.grabExcessHorizontalSpace = true; |
| // gd.horizontalAlignment = GridData.FILL; |
| // this.statusLabel.setLayoutData(gd); |
| |
| gd = new GridData(); |
| gd.grabExcessHorizontalSpace = true; |
| gd.grabExcessVerticalSpace = true; |
| gd.horizontalAlignment = GridData.FILL; |
| gd.verticalAlignment = GridData.FILL; |
| |
| vexWidget = new VexWidget(parentControl, SWT.V_SCROLL); |
| gd = new GridData(); |
| gd.grabExcessHorizontalSpace = true; |
| gd.grabExcessVerticalSpace = true; |
| gd.horizontalAlignment = GridData.FILL; |
| gd.verticalAlignment = GridData.FILL; |
| vexWidget.setLayoutData(gd); |
| |
| final MenuManager menuManager = new MenuManager(); |
| getSite().registerContextMenu("org.eclipse.wst.xml.vex.ui.popup", menuManager, vexWidget); |
| vexWidget.setMenu(menuManager.createContextMenu(vexWidget)); |
| |
| savedUndoDepth = vexWidget.getUndoDepth(); |
| |
| // new for scopes |
| final IContextService cs = (IContextService) getSite().getService(IContextService.class); |
| cs.activateContext("org.eclipse.wst.xml.vex.ui.VexEditorContext"); |
| |
| vexWidget.addSelectionChangedListener(selectionProvider); |
| |
| parentControl.layout(true); |
| |
| } |
| |
| private void handleResourceChanged(final IResourceDelta delta) { |
| |
| if (delta.getKind() == IResourceDelta.CHANGED) { |
| if ((delta.getFlags() & IResourceDelta.CONTENT) != 0) |
| handleResourceContentChanged(); |
| } else if (delta.getKind() == IResourceDelta.REMOVED) |
| if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) { |
| final IPath toPath = delta.getMovedToPath(); |
| final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(toPath); |
| setInput(new FileEditorInput(file)); |
| } else if (!isDirty()) |
| getEditorSite().getPage().closeEditor(this, false); |
| else |
| handleResourceDeleted(); |
| |
| } |
| |
| private void handleResourceContentChanged() { |
| |
| if (!isDirty()) |
| loadInput(); |
| else { |
| |
| final String message = MessageFormat.format(Messages.getString("VexEditor.docChanged.message"), //$NON-NLS-1$ |
| new Object[] { getEditorInput().getName() }); |
| |
| final MessageDialog dlg = new MessageDialog(getSite().getShell(), Messages.getString("VexEditor.docChanged.title"), //$NON-NLS-1$ |
| null, message, MessageDialog.QUESTION, new String[] { Messages.getString("VexEditor.docChanged.discard"), //$NON-NLS-1$ |
| Messages.getString("VexEditor.docChanged.overwrite") }, //$NON-NLS-1$ |
| 1); |
| |
| final int result = dlg.open(); |
| |
| if (result == 0) |
| loadInput(); |
| else |
| doSave(null); |
| } |
| } |
| |
| private void handleResourceDeleted() { |
| |
| final String message = MessageFormat.format(Messages.getString("VexEditor.docDeleted.message"), //$NON-NLS-1$ |
| new Object[] { getEditorInput().getName() }); |
| |
| final MessageDialog dlg = new MessageDialog(getSite().getShell(), Messages.getString("VexEditor.docDeleted.title"), //$NON-NLS-1$ |
| null, message, MessageDialog.QUESTION, new String[] { Messages.getString("VexEditor.docDeleted.discard"), //$NON-NLS-1$ |
| Messages.getString("VexEditor.docDeleted.save") }, //$NON-NLS-1$ |
| 1); |
| |
| final int result = dlg.open(); |
| |
| if (result == 0) |
| getEditorSite().getPage().closeEditor(this, false); |
| else { // Save |
| |
| doSaveAs(); |
| |
| // Check if they saved or not. If not, close the editor |
| if (!getEditorInput().exists()) |
| getEditorSite().getPage().closeEditor(this, false); |
| } |
| } |
| |
| // Listen for stylesheet changes and respond appropriately |
| private final IConfigListener configListener = new IConfigListener() { |
| public void configChanged(final ConfigEvent e) { |
| Display.getDefault().asyncExec(new Runnable() { |
| public void run() { |
| if (style == null) |
| return; |
| |
| final String styleId = style.getUniqueId(); |
| final Style newStyle = VexPlugin.getInstance().getConfigurationRegistry().getStyle(styleId); |
| if (newStyle == null) { |
| // Oops, style went bye-bye |
| // Let's just hold on to it in case it comes back later |
| } else { |
| vexWidget.setStyleSheet(newStyle.getStyleSheet()); |
| style = newStyle; |
| } |
| } |
| }); |
| } |
| |
| public void configLoaded(final ConfigEvent e) { |
| Display.getDefault().asyncExec(new Runnable() { |
| public void run() { |
| loadInput(); |
| } |
| }); |
| } |
| }; |
| |
| private final ISelectionChangedListener selectionChangedListener = new ISelectionChangedListener() { |
| public void selectionChanged(final SelectionChangedEvent event) { |
| if (isDirty() != wasDirty) { |
| firePropertyChange(IEditorPart.PROP_DIRTY); |
| wasDirty = isDirty(); |
| } |
| setStatus(getLocation()); |
| |
| // update dynamic UI element labels |
| final IEditorSite editorSite = VexEditor.this.getEditorSite(); |
| final IWorkbenchWindow window = editorSite.getWorkbenchWindow(); |
| if (window instanceof IServiceLocator) { |
| final IServiceLocator serviceLocator = window; |
| final ICommandService commandService = (ICommandService) serviceLocator.getService(ICommandService.class); |
| commandService.refreshElements(ConvertElementHandler.COMMAND_ID, null); |
| commandService.refreshElements(RemoveTagHandler.COMMAND_ID, null); |
| } |
| |
| // update context service |
| final ISourceProviderService service = (ISourceProviderService) window.getService(ISourceProviderService.class); |
| final DocumentContextSourceProvider contextProvider = (DocumentContextSourceProvider) service |
| .getSourceProvider(DocumentContextSourceProvider.IS_COLUMN); |
| contextProvider.fireUpdate(vexWidget); |
| } |
| }; |
| |
| private final EntityResolver entityResolver = new EntityResolver() { |
| public InputSource resolveEntity(final String publicId, final String systemId) throws SAXException, IOException { |
| |
| // System.out.println("### Resolving publicId " + publicId + |
| // ", systemId " + systemId); |
| |
| if (doctype == null) { |
| // |
| // If doctype hasn't already been set, this must be the doctype |
| // decl. |
| // |
| if (publicId != null) |
| doctype = VexPlugin.getInstance().getConfigurationRegistry().getDocumentType(publicId); |
| |
| if (doctype == null) { |
| final DocumentTypeSelectionDialog dlg = DocumentTypeSelectionDialog.create(getSite().getShell(), publicId); |
| dlg.open(); |
| doctype = dlg.getDoctype(); |
| updateDoctypeDecl = dlg.alwaysUseThisDoctype(); |
| |
| if (doctype == null) |
| throw new NoRegisteredDoctypeException(publicId); |
| } |
| |
| final URL url = doctype.getResourceUrl(); |
| |
| if (url == null) { |
| final String message = MessageFormat.format(Messages.getString("VexEditor.noUrlForDoctype"), //$NON-NLS-1$ |
| new Object[] { publicId }); |
| throw new RuntimeException(message); |
| } |
| |
| return new InputSource(url.toString()); |
| } else |
| return null; |
| } |
| }; |
| |
| private final IWhitespacePolicyFactory wsFactory = new IWhitespacePolicyFactory() { |
| public IWhitespacePolicy getPolicy(final String publicId) { |
| if (doctype == null) { |
| final DocumentTypeSelectionDialog dlg = DocumentTypeSelectionDialog.create(getSite().getShell(), publicId); |
| dlg.open(); |
| doctype = dlg.getDoctype(); |
| updateDoctypeDecl = dlg.alwaysUseThisDoctype(); |
| |
| if (doctype == null) |
| throw new NoRegisteredDoctypeException(null); |
| } |
| |
| style = VexEditor.getPreferredStyle(doctype.getPublicId()); |
| if (style == null) |
| throw new NoStyleForDoctypeException(); |
| |
| return new CssWhitespacePolicy(style.getStyleSheet()); |
| } |
| |
| }; |
| |
| private class ResourceChangeListener implements IResourceChangeListener { |
| |
| public void resourceChanged(final IResourceChangeEvent event) { |
| |
| if (saving) |
| return; |
| |
| final IPath path = ((IFileEditorInput) getEditorInput()).getFile().getFullPath(); |
| final IResourceDelta delta = event.getDelta().findMember(path); |
| if (delta != null) |
| Display.getDefault().asyncExec(new Runnable() { |
| public void run() { |
| handleResourceChanged(delta); |
| } |
| }); |
| } |
| |
| public void setSaving(final boolean saving) { |
| this.saving = saving; |
| } |
| |
| // Set to true so we can ignore change events while we're saving. |
| private boolean saving; |
| }; |
| |
| private final ResourceChangeListener resourceChangeListener = new ResourceChangeListener(); |
| |
| // |
| // wsFactory communicates failures back to init() through the XML parser |
| // by throwing one of these exceptions |
| // |
| |
| /** |
| * Indicates that no document type is registered for the public ID in the |
| * document, or that the document does not have a PUBLIC DOCTYPE decl, in |
| * which case publicId is null. |
| */ |
| private static class NoRegisteredDoctypeException extends RuntimeException { |
| private static final long serialVersionUID = 1L; |
| |
| public NoRegisteredDoctypeException(final String publicId) { |
| this.publicId = publicId; |
| } |
| |
| public String getPublicId() { |
| return publicId; |
| } |
| |
| private final String publicId; |
| } |
| |
| /** |
| * Indicates that the document was matched to a registered doctype, but that |
| * the given doctype does not have a matching style. |
| */ |
| private static class NoStyleForDoctypeException extends RuntimeException { |
| |
| private static final long serialVersionUID = 1L; |
| } |
| |
| private String getLocation() { |
| final List<String> path = new ArrayList<String>(); |
| VEXElement element = vexWidget.getCurrentElement(); |
| while (element != null) { |
| path.add(element.getName()); |
| element = element.getParent(); |
| } |
| Collections.reverse(path); |
| final StringBuffer sb = new StringBuffer(path.size() * 15); |
| for (final String part : path) { |
| sb.append("/"); //$NON-NLS-1$ |
| sb.append(part); |
| } |
| return sb.toString(); |
| } |
| |
| @Override |
| public Object getAdapter(final Class adapter) { |
| |
| if (adapter == IContentOutlinePage.class) |
| return new DocumentOutlinePage(); |
| else if (adapter == IPropertySheetPage.class) { |
| |
| final PropertySheetPage page = new PropertySheetPage(); |
| page.setPropertySourceProvider(new IPropertySourceProvider() { |
| public IPropertySource getPropertySource(final Object object) { |
| if (object instanceof Element) { |
| final IStructuredSelection sel = (IStructuredSelection) vexWidget.getSelection(); |
| final boolean multi = sel != null && sel.size() > 1; |
| final Validator validator = vexWidget.getDocument().getValidator(); |
| return new ElementPropertySource((VEXElement) object, validator, multi); |
| } else |
| return null; |
| } |
| }); |
| return page; |
| |
| } else if (adapter == IFindReplaceTarget.class) |
| return new AbstractRegExFindReplaceTarget() { |
| |
| @Override |
| protected int getSelectionStart() { |
| return getVexWidget().getSelectionStart(); |
| } |
| |
| @Override |
| protected int getSelectionEnd() { |
| return getVexWidget().getSelectionEnd(); |
| } |
| |
| @Override |
| protected void setSelection(final int start, final int end) { |
| getVexWidget().moveTo(start); |
| getVexWidget().moveTo(end, true); |
| } |
| |
| @Override |
| protected CharSequence getDocument() { |
| return new CharSequence() { |
| |
| public CharSequence subSequence(final int start, final int end) { |
| return doc.getRawText(start, end); |
| } |
| |
| public int length() { |
| return doc.getLength(); |
| } |
| |
| public char charAt(final int index) { |
| return doc.getCharacterAt(index); |
| } |
| }; |
| } |
| |
| @Override |
| protected void inDocumentReplaceSelection(final CharSequence text) { |
| final VexWidget vexWidget = getVexWidget(); |
| |
| // because of Undo this action must be atomic |
| vexWidget.beginWork(); |
| try { |
| vexWidget.deleteSelection(); |
| vexWidget.insertText(text.toString()); |
| } finally { |
| vexWidget.endWork(true); |
| } |
| } |
| |
| }; |
| else |
| return super.getAdapter(adapter); |
| } |
| |
| } |