/*****************************************************************************
* This file is part of Rinzo
*
* Author: Claudio Cancinos
* WWW: https://sourceforge.net/projects/editorxml
* Copyright (C): 2008, Claudio Cancinos
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; If not, see <http://www.gnu.org/licenses/>
****************************************************************************/
package ar.com.tadp.xml.rinzo.core;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.ResourceUtil;
import org.eclipse.ui.texteditor.ContentAssistAction;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import ar.com.tadp.xml.rinzo.XMLEditorPlugin;
import ar.com.tadp.xml.rinzo.core.actions.FormatAction;
import ar.com.tadp.xml.rinzo.core.eclipse.copies.MatchingCharacterPainter;
import ar.com.tadp.xml.rinzo.core.keyListeners.AutoInsertEndTagHandler;
import ar.com.tadp.xml.rinzo.core.keyListeners.CommentSelectionHandler;
import ar.com.tadp.xml.rinzo.core.keyListeners.NavigateTagsHandler;
import ar.com.tadp.xml.rinzo.core.keyListeners.NodeRangeHighlighter;
import ar.com.tadp.xml.rinzo.core.model.TreeModelContainer;
import ar.com.tadp.xml.rinzo.core.model.XMLExternalFileDocumentProvider;
import ar.com.tadp.xml.rinzo.core.model.XMLInternalFileDocumentProvider;
import ar.com.tadp.xml.rinzo.core.model.XMLNode;
import ar.com.tadp.xml.rinzo.core.model.XMLTreeModel;
import ar.com.tadp.xml.rinzo.core.model.tags.XMLTagDefinitionProvider;
import ar.com.tadp.xml.rinzo.core.model.tags.dtd.DTDTagDefinitionProvider;
import ar.com.tadp.xml.rinzo.core.model.tags.nodef.NoDefTagDefinitionProvider;
import ar.com.tadp.xml.rinzo.core.model.tags.xsd.CompositeXMLTagDefinitionProvider;
import ar.com.tadp.xml.rinzo.core.model.tags.xsd.XSDTagDefinitionProvider;
import ar.com.tadp.xml.rinzo.core.model.visitor.FoldingNodesVisitor;
import ar.com.tadp.xml.rinzo.core.outline.XMLOutlinePage;
import ar.com.tadp.xml.rinzo.core.resources.cache.DocumentCache;
import ar.com.tadp.xml.rinzo.core.resources.cache.DocumentStructureDeclaration;
import ar.com.tadp.xml.rinzo.core.utils.FileUtils;
/**
* Definition of the structure of Rinzo's editor
*
* @author ccancinos
*/
public class RinzoXMLEditor extends TextEditor implements ISelectionChangedListener {
/** The ID of this editor as defined in plugin.xml */
public static final String EDITOR_ID = "ar.com.tadp.xml.rinzo.core";
/** The ID of the editor context menu */
public static final String EDITOR_CONTEXT = EDITOR_ID + ".context";
/** The ID of the editor ruler context menu */
public static final String RULER_CONTEXT = EDITOR_CONTEXT + ".ruler";
private XMLOutlinePage outlinePage;
private NoDefTagDefinitionProvider containersRegistry = new NoDefTagDefinitionProvider();
private XMLTagDefinitionProvider schemaContaintersRegistry = null;
private DTDTagDefinitionProvider dtdContaintersRegistry;
private EditorConfiguration editorFileConfigurationStrategy;
private boolean changed = true;
private EditorUpdater updater;
private TagPairMatcher pairMatcher;
private ProjectionSupport projectionSupport;
private Annotation[] oldAnnotations;
private ProjectionAnnotationModel annotationModel;
protected void doSetInput(IEditorInput input) throws CoreException {
if (input instanceof IFileEditorInput || input instanceof IStorageEditorInput) {
setDocumentProvider(new XMLInternalFileDocumentProvider(this));
} else {
setDocumentProvider(new XMLExternalFileDocumentProvider(this));
}
this.setConfiturationStrategy(new XMLEditorConfiguration(this));
super.doSetInput(input);
updater = new EditorUpdater(this);
ThreadExecutorService.getInstance().execute(this.updater);
getDocumentProvider().getDocument(input).addDocumentListener(new IDocumentListener() {
public void documentAboutToBeChanged(DocumentEvent event) {
}
public void documentChanged(DocumentEvent event) {
changed = true;
}
});
}
public void dispose() {
this.updater.setStop();
}
protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
super.configureSourceViewerDecorationSupport(support);
this.pairMatcher = new TagPairMatcher(this);
support.setCharacterPairMatcher(pairMatcher);
support.setSymbolicFontName(getFontPropertyPreferenceKey());
}
private void setConfiturationStrategy(EditorConfiguration internalFileConfigurationStrategy) {
this.editorFileConfigurationStrategy = internalFileConfigurationStrategy;
this.setSourceViewerConfiguration((SourceViewerConfiguration) internalFileConfigurationStrategy);
}
protected void initializeEditor() {
super.initializeEditor();
setKeyBindingScopes(new String[] { "org.eclipse.ui.rinzoEditorScope" });
setPreferenceStore(XMLEditorPlugin.getDefault().getPreferenceStore());
setEditorContextMenuId(EDITOR_CONTEXT);
setRulerContextMenuId(RULER_CONTEXT);
}
public void createPartControl(Composite composite) {
super.createPartControl(composite);
StyledText styledtext = this.getSourceViewer().getTextWidget();
styledtext.addKeyListener(new AutoInsertEndTagHandler(this));
styledtext.addKeyListener(new CommentSelectionHandler(this));
styledtext.addKeyListener(new NavigateTagsHandler(this));
ITextViewerExtension2 extension = (ITextViewerExtension2) getSourceViewer();
MatchingCharacterPainter painter = new MatchingCharacterPainter(getSourceViewer(), new TagPairMatcher(this));
painter.setColor(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
extension.addPainter(painter);
((TextViewer) this.getSourceViewer()).addPostSelectionChangedListener(new NodeRangeHighlighter(this));
ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
projectionSupport = new ProjectionSupport(viewer, getAnnotationAccess(), getSharedColors());
projectionSupport.install();
// turn projection mode on
viewer.doOperation(ProjectionViewer.TOGGLE);
annotationModel = viewer.getProjectionAnnotationModel();
this.updateFoldingStructure();
}
public Object getAdapter(Class adapter) {
if (IContentOutlinePage.class.equals(adapter)) {
if (outlinePage == null) {
outlinePage = new XMLOutlinePage(this);
outlinePage.addSelectionChangedListener(this);
}
return outlinePage;
}
return super.getAdapter(adapter);
}
public ISourceViewer getSourceViewerEditor() {
return super.getSourceViewer();
}
public void doSave(IProgressMonitor progressMonitor) {
progressMonitor.beginTask("Saving File: " + this.getFileName(), 2);
try {
if (XMLEditorPlugin.isFormatOnSave()) {
FormatAction formatAction = new FormatAction();
formatAction.setActiveEditor(null, this);
formatAction.run(null);
}
super.doSave(new SubProgressMonitor(progressMonitor, 1));
progressMonitor.worked(1);
if (this.getModel().getTree().getRootNode() != null && this.getEditorInputIFile() != null) {
this.setChanged(true);
}
progressMonitor.worked(1);
if (!this.updater.isStop()) {
ThreadExecutorService.getInstance().execute(this.updater);
}
} catch (Exception e) {
XMLEditorPlugin.logErrorMessage("Error saving file: " + this.getFileName(), e);
} finally {
progressMonitor.done();
}
}
protected void editorContextMenuAboutToShow(IMenuManager menu) {
super.editorContextMenuAboutToShow(menu);
this.editorFileConfigurationStrategy.editorContextMenuAboutToShow(menu);
}
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
if (!selection.isEmpty()) {
Object element = selection.getFirstElement();
if (element != null && element instanceof XMLNode) {
XMLNode node = (XMLNode) element;
this.selectAndReveal(node.getSelectionOffset(), node.getSelectionLength());
}
}
}
public void setFocus() {
super.setFocus();
setKeyBindingScopes(new String[] { "org.eclipse.ui.rinzoEditorScope" });
}
protected void createActions() {
super.createActions();
Action action = new ContentAssistAction(XMLEditorPlugin.getDefault().getResourceBundle(),
"ContentAssistProposal.", this);
action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
setAction("ContentAssistProposal", action);
markAsStateDependentAction("ContentAssistProposal", true);
}
public String getFileName() {
return FileUtils.relPathToUrl(this.getEditorInputFile().getAbsolutePath());
}
public IFile getEditorInputIFile() {
return ResourceUtil.getFile(this.getEditorInput());
}
public File getEditorInputFile() {
File toFile = null;
IEditorInput input = this.getEditorInput();
if (input instanceof FileStoreEditorInput) {
toFile = new File(((FileStoreEditorInput) input).getURI());
}
if (input instanceof IPathEditorInput) {
toFile = ((IPathEditorInput) input).getPath().toFile();
}
return toFile;
}
public String getLineSeparator() {
return FileUtils.getLineSeparator(this);
}
public XMLTreeModel getModel() {
return ((TreeModelContainer) this.getDocumentProvider()).getTreeModel();
}
public NoDefTagDefinitionProvider getCodeTagContainersRegistry() {
return this.containersRegistry;
}
public XMLTagDefinitionProvider getTagContainersRegistry() {
try {
TreeModelContainer documentProvider = (TreeModelContainer) this.getDocumentProvider();
XMLTreeModel treeModel = documentProvider.getTreeModel();
Collection<DocumentStructureDeclaration> schemaDefinitions = treeModel.getSchemaDefinitions();
DocumentStructureDeclaration structureDeclaration = treeModel.getDTDDefinition();
XMLTagDefinitionProvider registry = this.containersRegistry;
if (this.getFileName() != null) {
if (!schemaDefinitions.isEmpty()) {
registry = this.getCompositeSchemaTagContainersRegistry(schemaDefinitions);
} else {
if (structureDeclaration != null) {
Collection<DocumentStructureDeclaration> definitions = new ArrayList<DocumentStructureDeclaration>();
definitions.add(structureDeclaration);
DocumentCache.getInstance().getAllLocations(definitions, this.getFileName());
registry = this.getDTDTagContainersRegistry(treeModel.getDTDRootNode(), structureDeclaration);
}
}
}
return registry;
} catch (Exception exception) {
return this.containersRegistry;
}
}
private DTDTagDefinitionProvider getDTDTagContainersRegistry(String rootNodeName,
DocumentStructureDeclaration structureDeclaration) {
if (this.dtdContaintersRegistry == null) {
this.dtdContaintersRegistry = new DTDTagDefinitionProvider(this.getFileName(), rootNodeName,
structureDeclaration);
} else {
this.dtdContaintersRegistry.setDefinition(this.getFileName(), structureDeclaration);
}
return this.dtdContaintersRegistry;
}
private XMLTagDefinitionProvider getCompositeSchemaTagContainersRegistry(
Collection<DocumentStructureDeclaration> schemaDefinitions) throws URISyntaxException {
DocumentCache.getInstance().getAllLocations(schemaDefinitions, this.getFileName());
if (this.schemaContaintersRegistry == null) {
CompositeXMLTagDefinitionProvider compositeXMLTagDefinitionProvider = new CompositeXMLTagDefinitionProvider();
for (DocumentStructureDeclaration structureDeclaration : schemaDefinitions) {
try {
compositeXMLTagDefinitionProvider.addTagDefinitionProvider(new XSDTagDefinitionProvider(this
.getFileName(), structureDeclaration, schemaDefinitions));
} catch (Exception e) {
e.printStackTrace();
}
}
this.schemaContaintersRegistry = compositeXMLTagDefinitionProvider;
} else {
Collection<URI> uris = new ArrayList<URI>();
CompositeXMLTagDefinitionProvider compositeXMLTagDefinitionProvider = (CompositeXMLTagDefinitionProvider) this.schemaContaintersRegistry;
for (DocumentStructureDeclaration structureDeclaration : schemaDefinitions) {
try {
URI schemaURI = FileUtils.resolveURI(this.getFileName(), structureDeclaration.getSystemId());
uris.add(schemaURI);
} catch (Exception e) {
e.printStackTrace();
}
}
compositeXMLTagDefinitionProvider.setDefinition(this.getFileName(), uris);
}
return (XMLTagDefinitionProvider) this.schemaContaintersRegistry;
}
public synchronized boolean isChanged() {
return changed;
}
public synchronized void setChanged(boolean changed) {
this.changed = changed;
}
public void updateFoldingStructure() {
FoldingNodesVisitor visitor = new FoldingNodesVisitor(this.getSourceViewerEditor().getDocument(),
this.getLineSeparator());
this.getModel().getTree().accept(visitor);
HashMap<ProjectionAnnotation, Position> newAnnotations = visitor.getAnnotationsMap();
Annotation[] annotations = visitor.getAnnotations();
if (annotationModel != null) {
annotationModel.modifyAnnotations(oldAnnotations, newAnnotations, null);
}
oldAnnotations = annotations;
}
protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
ISourceViewer viewer = new RinzoProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(),
styles);
// ensure decoration support has been created and configured.
getSourceViewerDecorationSupport(viewer);
return viewer;
}
@Override
protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
if (this.getSourceViewer().getTextWidget() != null) {
super.handlePreferenceStoreChanged(event);
}
}
@Override
protected String[] collectContextMenuPreferencePages() {
String[] defaultPages = super.collectContextMenuPreferencePages();
String[] rinzoPages = new String[] { "ar.com.tadp.xml.rinzo.core.preferences.FormattingPreferencePage", //$NON-NLS-1$
"ar.com.tadp.xml.rinzo.core.preferences.MainPreferencePage", //$NON-NLS-1$
"ar.com.tadp.xml.rinzo.core.preferences.TemplatesPreferencePage", //$NON-NLS-1$
"ar.com.tadp.xml.rinzo.core.preferences.SyntaxColorPreferencePage", //$NON-NLS-1$
"ar.com.tadp.xml.rinzo.jdt.preferences.ClassNamesPreferencePage", //$NON-NLS-1$
};
int rinzoPagesLength = rinzoPages.length;
int defaultPagesLength = defaultPages.length;
String pages[] = new String[rinzoPagesLength + defaultPagesLength];
System.arraycopy(rinzoPages, 0, pages, 0, rinzoPagesLength);
System.arraycopy(defaultPages, 0, pages, rinzoPagesLength, defaultPagesLength);
return pages;
}
}