org.eclipse.wb.internal.xwt.parser.XwtParser.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wb.internal.xwt.parser.XwtParser.java

Source

/*******************************************************************************
 * Copyright (c) 2011 Google, Inc.
 * 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:
 *    Google, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.wb.internal.xwt.parser;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import org.eclipse.wb.core.model.broadcast.ObjectInfoTreeComplete;
import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils;
import org.eclipse.wb.internal.core.utils.execution.RunnableEx;
import org.eclipse.wb.internal.core.utils.execution.RunnableObjectEx;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.wb.internal.core.utils.xml.DocumentElement;
import org.eclipse.wb.internal.core.utils.xml.DocumentModelVisitor;
import org.eclipse.wb.internal.core.xml.model.EditorContext;
import org.eclipse.wb.internal.core.xml.model.XmlObjectInfo;
import org.eclipse.wb.internal.core.xml.model.creation.CreationSupport;
import org.eclipse.wb.internal.core.xml.model.creation.ElementCreationSupport;
import org.eclipse.wb.internal.core.xml.model.utils.GlobalStateXml;
import org.eclipse.wb.internal.core.xml.model.utils.XmlObjectUtils;
import org.eclipse.wb.internal.xwt.model.util.NamePropertySupport;
import org.eclipse.wb.internal.xwt.model.util.NameSupport;
import org.eclipse.wb.internal.xwt.model.util.XwtListenerProperties;
import org.eclipse.wb.internal.xwt.model.util.XwtStaticFieldSupport;
import org.eclipse.wb.internal.xwt.model.util.XwtStringArraySupport;
import org.eclipse.wb.internal.xwt.model.util.XwtTagResolver;

import org.eclipse.core.resources.IFile;
import org.eclipse.e4.xwt.DefaultLoadingContext;
import org.eclipse.e4.xwt.ILoadingContext;
import org.eclipse.e4.xwt.IXWTLoader;
import org.eclipse.e4.xwt.XWT;
import org.eclipse.e4.xwt.XWTLoaderManager;
import org.eclipse.e4.xwt.core.IElementLoaderFactory;
import org.eclipse.e4.xwt.core.IRenderingContext;
import org.eclipse.e4.xwt.core.IVisualElementLoader;
import org.eclipse.e4.xwt.forms.XWTForms;
import org.eclipse.e4.xwt.internal.core.Core;
import org.eclipse.e4.xwt.internal.xml.Element;
import org.eclipse.e4.xwt.javabean.ResourceLoader;
import org.eclipse.e4.xwt.javabean.metadata.Metaclass;
import org.eclipse.e4.xwt.metadata.IMetaclass;
import org.eclipse.jface.text.IDocument;
import org.eclipse.swt.widgets.Button;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import java.net.URL;
import java.util.Map;
import java.util.Set;

/**
 * Parser for XWT UI.
 * 
 * @author scheglov_ke
 * @coverage XWT.parser
 */
public final class XwtParser {
    private final EditorContext m_context;
    private final IFile m_file;
    private final IDocument m_document;
    private final Map<String, DocumentElement> m_pathToElementMap = Maps.newHashMap();
    private final Map<String, XmlObjectInfo> m_pathToModelMap = Maps.newHashMap();
    private XmlObjectInfo m_rootModel;

    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    public XwtParser(IFile file, IDocument document) throws Exception {
        m_file = file;
        m_document = document;
        m_context = new XwtEditorContext(file, m_document);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Parse
    //
    ////////////////////////////////////////////////////////////////////////////
    public XmlObjectInfo parse() throws Exception {
        return ExecutionUtils.runDesignTime(new RunnableObjectEx<XmlObjectInfo>() {
            public XmlObjectInfo runObject() throws Exception {
                return parse0();
            }
        });
    }

    private XmlObjectInfo parse0() throws Exception {
        m_context.initialize();
        m_context.setParsing(true);
        fillMap_pathToElement();
        // notifications from model
        m_context.getBroadcastSupport().addListener(null, new XwtParserBindToElement() {
            public void invoke(XmlObjectInfo object, DocumentElement element) {
                String path = getPath(element);
                m_pathToModelMap.put(path, object);
            }
        });
        // handler for creation events
        Core profile = new Core(new IElementLoaderFactory() {
            private int m_level;

            public IVisualElementLoader createElementLoader(IRenderingContext context, IXWTLoader loader) {
                return new ResourceLoader(context, loader) {
                    @Override
                    protected void postCreation0(final Element element, final Object targetObject) {
                        if (m_level > 1) {
                            return;
                        }
                        ExecutionUtils.runRethrow(new RunnableEx() {
                            public void run() throws Exception {
                                postCreationEx(element, targetObject);
                            }
                        });
                    }

                    private void postCreationEx(Element element, Object targetObject) throws Exception {
                        String path = getPath(element);
                        DocumentElement xmlElement = m_pathToElementMap.get(path);
                        if (xmlElement == null) {
                            return;
                        }
                        // create model
                        XmlObjectInfo objectInfo = createObjectInfo(targetObject, xmlElement);
                        if (objectInfo == null) {
                            return;
                        }
                        m_pathToModelMap.put(path, objectInfo);
                        // add to hierarchy
                        if ("0".equals(path)) {
                            m_rootModel = objectInfo;
                        } else {
                            XmlObjectInfo parentObjectInfo = getParentObjectInfo(element);
                            if (parentObjectInfo != null) {
                                parentObjectInfo.addChild(objectInfo);
                            }
                        }
                    }

                    ////////////////////////////////////////////////////////////////////////////
                    //
                    // Tweaks for handling tested XWT files
                    //
                    ////////////////////////////////////////////////////////////////////////////
                    private final Set<Element> m_processedElements = Sets.newHashSet();

                    private boolean isRoot(Element element) {
                        if (!m_processedElements.contains(element)) {
                            m_processedElements.add(element);
                            return "0".equals(element.getPath());
                        }
                        return false;
                    }

                    @Override
                    protected Object doCreate(Object parent, Element element, Class<?> constraintType,
                            Map<String, Object> options) throws Exception {
                        boolean isRoot = isRoot(element);
                        try {
                            if (isRoot) {
                                m_level++;
                            }
                            return super.doCreate(parent, element, constraintType, options);
                        } finally {
                            if (isRoot) {
                                m_level--;
                            }
                        }
                    }
                };
            }
        }, XWTLoaderManager.getActive());
        // render, with parsing
        XWT.applyProfile(profile);
        ILoadingContext _loadingContext = XWT.getLoadingContext();
        XWT.setLoadingContext(new DefaultLoadingContext(m_context.getClassLoader()));
        try {
            URL url = m_file.getLocationURI().toURL();
            String content = m_document.get();
            Map<String, Object> options = Maps.newHashMap();
            options.put(IXWTLoader.DESIGN_MODE_PROPERTY, Boolean.TRUE);
            configureForForms(m_context, options);
            XWT.loadWithOptions(IOUtils.toInputStream(content), url, options);
        } finally {
            XWT.setLoadingContext(_loadingContext);
            XWT.restoreProfile();
        }
        // done
        m_context.setParsing(false);
        NameSupport.decoratePresentationWithName(m_rootModel);
        XmlObjectUtils.callRootProcessors(m_rootModel);
        new XwtTagResolver(m_rootModel);
        new XwtStringArraySupport(m_rootModel);
        new XwtStaticFieldSupport(m_rootModel);
        new XwtListenerProperties(m_rootModel);
        new NamePropertySupport(m_rootModel);
        GlobalStateXml.activate(m_rootModel);
        m_rootModel.getBroadcast(ObjectInfoTreeComplete.class).invoke();
        m_rootModel.refresh_dispose();
        return m_rootModel;
    }

    /**
     * Visits all {@link DocumentElement}s and remembers all of them with path.
     */
    private void fillMap_pathToElement() {
        m_context.getRootElement().accept(new DocumentModelVisitor() {
            @Override
            public void endVisit(DocumentElement element) {
                m_pathToElementMap.put(getPath(element), element);
            }
        });
    }

    private XmlObjectInfo createObjectInfo(Object targetObject, DocumentElement element) throws Exception {
        XmlObjectInfo objectInfo;
        {
            Class<?> componentClass = targetObject.getClass();
            CreationSupport creationSupport = new ElementCreationSupport(element);
            objectInfo = XmlObjectUtils.createObject(m_context, componentClass, creationSupport);
            GlobalStateXml.activate(objectInfo);
        }
        // check if model should be created
        if (!XmlObjectUtils.hasTrueParameter(objectInfo, "XWT.hasModel")) {
            return null;
        }
        // done
        objectInfo.setObject(targetObject);
        return objectInfo;
    }

    private XmlObjectInfo getParentObjectInfo(Element element) {
        XmlObjectInfo parent = null;
        String path = element.getPath();
        do {
            path = StringUtils.substringBeforeLast(path, "/");
            parent = m_pathToModelMap.get(path);
        } while (parent == null && path.contains("/"));
        return parent;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Forms API
    //
    ////////////////////////////////////////////////////////////////////////////
    private static final String[] FORMS_CLASSES = { "org.eclipse.e4.xwt.forms.metaclass.FormMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.ButtonMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.CompositeMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.ExpandableCompositeMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.FormMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.FormTextMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.HyperlinkMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.ImageHyperlinkMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.LabelMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.ScrolledFormMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.ScrolledPageBookMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.SectionMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.TableMetaclass",
            "org.eclipse.e4.xwt.forms.metaclass.TextMetaclass" };

    /**
     * Configures XWT and options for Forms API support.
     */
    static void configureForForms(EditorContext context, Map<String, Object> options) throws Exception {
        if (!hasForms(context)) {
            XWT.registerMetaclass(new Metaclass(Button.class, null));
            return;
        }
        // install Forms decoration action
        {
            Object createdAction = ReflectionUtils.getFieldObject(XWTForms.class, "CreatedAction");
            options.put(IXWTLoader.CREATED_CALLBACK, createdAction);
        }
        // register IMetaclass-s
        for (String className : FORMS_CLASSES) {
            Class<?> clazz = XWTForms.class.getClassLoader().loadClass(className);
            IMetaclass metaclass = (IMetaclass) clazz.newInstance();
            XWT.registerMetaclass(metaclass);
        }
    }

    public static boolean hasForms(EditorContext context) {
        return context.getDocument().get().contains("Forms API");
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Path
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the path of our {@link DocumentElement}. It has same format as
     *         {@link #getPath(Element)}.
     */
    static String getPath(DocumentElement element) {
        DocumentElement parent = element.getParent();
        if (parent == null) {
            return "0";
        } else {
            int index = parent.indexOf(element);
            return getPath(parent) + "/" + index;
        }
    }

    /**
     * @return the path of XWT element. It has same format as {@link #getPath(DocumentElement)}.
     */
    static String getPath(Element element) {
        return element.getPath();
    }
}