Package org.eclipse.sapphire.ui.swt.xml.editor

Source Code of org.eclipse.sapphire.ui.swt.xml.editor.XmlEditorResourceStore

/******************************************************************************
* Copyright (c) 2014 Oracle and Liferay
* 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:
*    Konstantin Komissarchik - initial implementation and ongoing maintenance
*    Gregory Amerson - [371697] ClassCastException in XmlEditorResourceStore for non-local files
******************************************************************************/

package org.eclipse.sapphire.ui.swt.xml.editor;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.sapphire.Element;
import org.eclipse.sapphire.ElementList;
import org.eclipse.sapphire.ElementProperty;
import org.eclipse.sapphire.ImpliedElementProperty;
import org.eclipse.sapphire.ListProperty;
import org.eclipse.sapphire.Property;
import org.eclipse.sapphire.PropertyDef;
import org.eclipse.sapphire.Resource;
import org.eclipse.sapphire.ValueProperty;
import org.eclipse.sapphire.modeling.ByteArrayResourceStore;
import org.eclipse.sapphire.modeling.ResourceStoreException;
import org.eclipse.sapphire.modeling.ValidateEditException;
import org.eclipse.sapphire.modeling.xml.XmlElement;
import org.eclipse.sapphire.modeling.xml.XmlNode;
import org.eclipse.sapphire.modeling.xml.XmlResource;
import org.eclipse.sapphire.modeling.xml.XmlResourceStore;
import org.eclipse.sapphire.modeling.xml.XmlValueBindingImpl;
import org.eclipse.sapphire.ui.DelayedTasksExecutor;
import org.eclipse.sapphire.ui.SapphireEditor;
import org.eclipse.sapphire.ui.SourceEditorService;
import org.eclipse.sapphire.util.ListFactory;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
import org.eclipse.wst.sse.ui.StructuredTextEditor;
import org.eclipse.wst.sse.ui.internal.provisional.extensions.ISourceEditingTextTools;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.ui.internal.provisional.IDOMSourceEditingTextTools;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/

@SuppressWarnings( "restriction" )

public class XmlEditorResourceStore extends XmlResourceStore
{
    private SapphireEditor sapphireEditor;
    private StructuredTextEditor sourceEditor;
    private Element rootModelElement;
    private final Map<Node,Object> xmlNodeToModelElementsMap;
    private final INodeAdapter xmlNodeListener;
    private Scrubber scrubber;
    private final XmlSourceEditorService sourceEditorService;
   
    public XmlEditorResourceStore( final SapphireEditor sapphireEditor, final StructuredTextEditor sourceEditor )
    {
        super( (ByteArrayResourceStore) null );
       
        this.sapphireEditor = sapphireEditor;
        this.sourceEditor = sourceEditor;
        this.rootModelElement = null;
        this.xmlNodeToModelElementsMap = new IdentityHashMap<Node,Object>();
        this.sourceEditorService = new XmlSourceEditorService();
       
        final ISourceEditingTextTools sourceEditingTextTools = (ISourceEditingTextTools) this.sourceEditor.getAdapter( ISourceEditingTextTools.class );
        final IDOMSourceEditingTextTools domSourceEditingTextTools = (IDOMSourceEditingTextTools) sourceEditingTextTools;
       
        setDomDocument( domSourceEditingTextTools.getDOMDocument() );
       
        this.xmlNodeListener = new INodeAdapter()
        {
            public boolean isAdapterForType( final Object type )
            {
                return false;
            }

            public void notifyChanged( final INodeNotifier notifier,
                                       final int eventType,
                                       final Object changedFeature,
                                       final Object oldValue,
                                       final Object newValue,
                                       final int pos )
            {
                if( eventType == INodeNotifier.ADD && newValue instanceof IDOMNode )
                {
                    attachXmlNodeListener( (IDOMNode) newValue );
                }
               
                DelayedTasksExecutor.schedule( new RefreshElementsTask( getModelElements( (Node) notifier ) ) );
            }
        };

        attachXmlNodeListener();
    }
   
    public final SapphireEditor getEditor()
    {
        return this.sapphireEditor;
    }
   
    public final StructuredTextEditor getXmlEditor()
    {
        return this.sourceEditor;
    }

    @Override
    public boolean isXmlDeclarationNeeded()
    {
        return true;
    }

    @Override
    public void save() throws ResourceStoreException
    {
        final IEditorInput input = this.sourceEditor.getEditorInput();
       
        if( input instanceof FileEditorInput )
        {
            final IFile file = ( (FileEditorInput) input ).getFile();
           
            if( ! file.exists() )
            {
                return;
            }
        }

        validateSave();
        this.sourceEditor.doSave( new NullProgressMonitor() );
    }

    @Override
    public void validateEdit()
    {
        final IEditorInput input = this.sourceEditor.getEditorInput();
       
        if( input instanceof FileEditorInput )
        {
            final IFile file = ( (FileEditorInput) input ).getFile();
           
            if( ! file.exists() )
            {
                final IStatus st = ResourcesPlugin.getWorkspace().validateEdit( new IFile[] { file }, IWorkspace.VALIDATE_PROMPT );
               
                if( st.getSeverity() == IStatus.ERROR )
                {
                    throw new ValidateEditException();
                }
               
                try
                {
                    file.create( new ByteArrayInputStream( new byte[ 0 ] ), true, new NullProgressMonitor() );
                }
                catch( CoreException e )
                {
                    throw new ValidateEditException( e );
                }
            }
        }
       
        if( this.sourceEditor.validateEditorInputState() == false )
        {
            throw new ValidateEditException();
        }
    }
   
    @Override
    public void validateSave()
    {
        if( this.sourceEditor.validateEditorInputState() == false )
        {
            throw new ValidateEditException();
        }
    }
   
    @Override
    public <A> A adapt( final Class<A> adapterType )
    {
        A result = null;
       
        if( adapterType == ITextEditor.class )
        {
            result = adapterType.cast( getXmlEditor() );
        }
        else if( adapterType == SourceEditorService.class )
        {
            result = adapterType.cast( this.sourceEditorService );
        }
        else if( adapterType == SapphireEditor.class )
        {
            result = adapterType.cast( this.sapphireEditor );
        }
        else
        {
            result = super.adapt( adapterType );
        }
       
        return result;
    }

    @Override
    public void registerRootModelElement( final Element rootModelElement )
    {
        this.rootModelElement = rootModelElement;
    }

    @Override
    @SuppressWarnings( "unchecked" )
   
    public void registerModelElement( final Node xmlNode, final Element element )
    {
        synchronized( this.xmlNodeToModelElementsMap )
        {
            if( this.scrubber == null )
            {
                this.scrubber = new Scrubber();
                this.scrubber.start();
            }
           
            final Object object = this.xmlNodeToModelElementsMap.get( xmlNode );
           
            if( object == null )
            {
                this.xmlNodeToModelElementsMap.put( xmlNode, element );
            }
            else if( object instanceof Element )
            {
                if( object == element )
                {
                    return;
                }
                else
                {
                    this.xmlNodeToModelElementsMap.put( xmlNode, ListFactory.<Element>start().add( (Element) object ).add( element ).result() );
                }
            }
            else
            {
                final List<Element> list = (List<Element>) object;
               
                for( final Object obj : list )
                {
                    if( obj == element )
                    {
                        return;
                    }
                }
               
                this.xmlNodeToModelElementsMap.put( xmlNode, ListFactory.<Element>start().add( list ).add( element ).result() );
            }
        }
    }
   
    @Override
    public void unregisterModelElement( final Node xmlNode, final Element element )
    {
        synchronized( this.xmlNodeToModelElementsMap )
        {
            Object object = this.xmlNodeToModelElementsMap.get( xmlNode );
           
            if( object != null )
            {
                if( object instanceof Element )
                {
                    if( object == element )
                    {
                        this.xmlNodeToModelElementsMap.remove( xmlNode );
                    }
                }
                else
                {
                    final List<?> originalList = (List<?>) object;
                    final ListFactory<Element> modifiedListFactory = ListFactory.start();
                   
                    for( final Object entry : originalList )
                    {
                        if( entry != element )
                        {
                            modifiedListFactory.add( (Element) entry );
                        }
                    }
                   
                    final int modifiedListSize = modifiedListFactory.size();
                   
                    if( originalList.size() != modifiedListSize )
                    {
                        if( modifiedListSize == 1 )
                        {
                            this.xmlNodeToModelElementsMap.put( xmlNode, modifiedListFactory.get( 0 ) );
                        }
                        else
                        {
                            this.xmlNodeToModelElementsMap.put( xmlNode, modifiedListFactory.result() );
                        }
                    }
                }
            }
        }
    }

    @Override
    public void dispose()
    {
        super.dispose();
       
        detachXmlNodeListener();
       
        if( this.scrubber != null )
        {
            this.scrubber.dispose();
            this.scrubber = null;
        }
    }

    @SuppressWarnings( "unchecked" )
   
    public final List<Element> getModelElements( final Node xmlNode )
    {
        final List<Element> elements;
       
        synchronized( this.xmlNodeToModelElementsMap )
        {
            Node node = xmlNode;
            Object object = this.xmlNodeToModelElementsMap.get( node );
           
            while( object == null && node != null && ! ( node instanceof Document ) )
            {
                node = node.getParentNode();
                object = this.xmlNodeToModelElementsMap.get( node );
            }
           
            if( object == null )
            {
                elements = ListFactory.singleton( this.rootModelElement );
            }
            else if( object instanceof Element )
            {
                if( node.getParentNode() instanceof Document )
                {
                    elements = ListFactory.<Element>start().add( this.rootModelElement ).add( (Element) object ).result();
                }
                else
                {
                    elements = ListFactory.singleton( (Element) object );
                }
            }
            else
            {
                if( node.getParentNode() instanceof Document )
                {
                    elements = ListFactory.<Element>start().add( this.rootModelElement ).add( (List<Element>) object ).result();
                }
                else
                {
                    elements = (List<Element>) object;
                }
            }
        }
       
        return elements;
    }
   
    private void attachXmlNodeListener()
    {
        attachXmlNodeListener( (IDOMNode) getDomDocument() );
    }
   
    private void attachXmlNodeListener( final IDOMNode node )
    {
        node.addAdapter( this.xmlNodeListener );
       
        final NodeList children = node.getChildNodes();
       
        for( int i = 0, n = children.getLength(); i < n; i++ )
        {
            attachXmlNodeListener( (IDOMNode) children.item( i ) );
        }
    }
   
    private void detachXmlNodeListener()
    {
        detachXmlNodeListener( (IDOMNode) getDomDocument() );
    }
   
    private void detachXmlNodeListener( final IDOMNode node )
    {
        node.removeAdapter( this.xmlNodeListener );
       
        final NodeList children = node.getChildNodes();
       
        for( int i = 0, n = children.getLength(); i < n; i++ )
        {
            detachXmlNodeListener( (IDOMNode) children.item( i ) );
        }
    }
   
    private static final class RefreshElementsTask extends DelayedTasksExecutor.Task
    {
        private final List<Element> elements;
       
        public RefreshElementsTask( final List<Element> elements )
        {
            this.elements = elements;
        }
       
        @Override
        public boolean equals( final Object obj )
        {
            if( obj != null && obj instanceof RefreshElementsTask )
            {
                return ( this.elements.equals( ( (RefreshElementsTask) obj ).elements ) );
            }
           
            return false;
        }
       
        @Override
        public int hashCode()
        {
            return this.elements.hashCode();
        }
       
        public void run()
        {
            for( final Element element : this.elements )
            {
                if( ! element.disposed() )
                {
                    element.refresh();
                }
            }
        }
    }

    private final class Scrubber extends Thread
    {
        private boolean stopRequested = false;
       
        public void run()
        {
            final Map<Node,Object> xmlNodeToModelElementsMap = XmlEditorResourceStore.this.xmlNodeToModelElementsMap;
           
            while( true )
            {
                try
                {
                    sleep( 10000 );
                }
                catch( InterruptedException e ) {}
               
                synchronized( this )
                {
                    if( this.stopRequested == true )
                    {
                        return;
                    }
                }
               
                synchronized( xmlNodeToModelElementsMap )
                {
                    for( final Iterator<Node> itr = xmlNodeToModelElementsMap.keySet().iterator(); itr.hasNext(); )
                    {
                        if( itr.next().getParentNode() == null )
                        {
                            itr.remove();
                        }
                    }
                }
            }
        }
       
        public synchronized void dispose()
        {
            this.stopRequested = true;
            interrupt();
        }
    }
   
    private final class XmlSourceEditorService extends SourceEditorService
    {
        @Override
        public boolean find( final Element element,
                             final PropertyDef property )
        {
            return ( element.resource() instanceof XmlResource );
        }
       
        @Override
        public void show( final Element element,
                          final PropertyDef property )
        {
            final ITextEditor sourceView = getXmlEditor();
            final Range range = new Range();
           
            if( property != null )
            {
                final List<XmlNode> xmlNodes = getXmlNodes( element, property );
               
                if( ! xmlNodes.isEmpty() )
                {
                    if( property instanceof ValueProperty )
                    {
                        final IDOMNode domNode = (IDOMNode) xmlNodes.get( 0 ).getDomNode();
                       
                        if( domNode instanceof IDOMElement )
                        {
                            final IDOMElement domElement = (IDOMElement) domNode;
                           
                            if( domElement.hasEndTag() )
                            {
                                range.merge( domElement.getStartEndOffset(), domElement.getEndStartOffset() );
                            }
                            else
                            {
                                range.merge( domNode.getStartOffset(), domNode.getEndOffset() );
                            }
                        }
                        else if( domNode instanceof IDOMAttr )
                        {
                            final IDOMAttr domAttr = (IDOMAttr) domNode;
                            final int start = domAttr.getValueRegionStartOffset();
                            range.merge( start + 1, start + domAttr.getValueRegionText().length() - 1 );
                        }
                        else
                        {
                            range.merge( domNode.getStartOffset(), domNode.getEndOffset() );
                        }
                    }
                    else
                    {
                        for( XmlNode xmlNode : xmlNodes )
                        {
                            final IDOMNode domNode = (IDOMNode) xmlNode.getDomNode();
                            range.merge( domNode.getStartOffset(), domNode.getEndOffset() );
                        }
                    }
                }
            }
           
            if( ! range.initialized() )
            {
                Element modElement = element;
                Resource resource = modElement.resource();
                XmlElement xmlElement = null;
               
                if( resource != null )
                {
                    xmlElement = ( (XmlResource) resource ).getXmlElement();
                }
               
                while( xmlElement == null && modElement != null )
                {
                    final Property parent = modElement.parent();
                   
                    if( parent == null )
                    {
                        modElement = null;
                    }
                    else
                    {
                        modElement = parent.element();
                        resource = modElement.resource();
                       
                        if( resource != null )
                        {
                            xmlElement = ( (XmlResource) resource ).getXmlElement();
                        }
                    }
                }
                   
                if( xmlElement != null )
                {
                    final IDOMNode domNode = (IDOMNode) xmlElement.getDomNode();
                    range.merge( domNode.getStartOffset(), domNode.getEndOffset() );
                }
            }
           
            final TextSelection textSelection
                = ( range.initialized() ? new TextSelection( range.start(), range.end() - range.start() ) : null );

            sourceView.getSelectionProvider().setSelection( textSelection );
           
            getEditor().showPage( sourceView );
        }
       
        private List<XmlNode> getXmlNodes( final Element element,
                                           final PropertyDef property )
        {
            if( property instanceof ListProperty )
            {
                final ElementList<?> list = element.property( (ListProperty) property );
                final List<XmlNode> xmlNodes = new ArrayList<XmlNode>( list.size() );
               
                for( Element entry : list )
                {
                    final Resource resource = entry.resource();
                   
                    if( resource instanceof XmlResource )
                    {
                        final XmlNode xmlNode = ( (XmlResource) resource ).getXmlElement();
                       
                        if( xmlNode != null )
                        {
                            xmlNodes.add( xmlNode );
                        }
                    }
                }
               
                return xmlNodes;
            }
            else if( property instanceof ElementProperty && ! ( property instanceof ImpliedElementProperty ) )
            {
                final Element child = element.property( (ElementProperty) property ).content();
               
                if( child != null )
                {
                    final Resource resource = child.resource();
                   
                    if( resource instanceof XmlResource )
                    {
                        final XmlNode xmlNode = ( (XmlResource) resource ).getXmlElement();
                       
                        if( xmlNode != null )
                        {
                            return Collections.singletonList( xmlNode );
                        }
                    }
                }
            }
            else
            {
                final Resource resource = element.resource();
               
                if( resource instanceof XmlResource )
                {
                    final XmlResource r = (XmlResource) resource;
                    final XmlNode xmlNode = ( (XmlValueBindingImpl) r.binding( element.property( property ) ) ).getXmlNode();
                   
                    if( xmlNode != null )
                    {
                        return Collections.singletonList( xmlNode );
                    }
                }
            }
           
            return Collections.emptyList();
        }
    }
   
}
TOP

Related Classes of org.eclipse.sapphire.ui.swt.xml.editor.XmlEditorResourceStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.