Package com.salesforce.ide.ui.packagemanifest

Source Code of com.salesforce.ide.ui.packagemanifest.PackageManifestController

/*******************************************************************************
* Copyright (c) 2014 Salesforce.com, 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:
*     Salesforce.com, inc. - initial API and implementation
******************************************************************************/
package com.salesforce.ide.ui.packagemanifest;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.log4j.Logger;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.salesforce.ide.core.factories.FactoryException;
import com.salesforce.ide.core.internal.context.ContainerDelegate;
import com.salesforce.ide.core.internal.controller.Controller;
import com.salesforce.ide.core.internal.utils.Constants;
import com.salesforce.ide.core.internal.utils.DialogUtils;
import com.salesforce.ide.core.internal.utils.ForceExceptionUtils;
import com.salesforce.ide.core.internal.utils.PackageManifestDocumentUtils;
import com.salesforce.ide.core.internal.utils.Utils;
import com.salesforce.ide.core.model.Component;
import com.salesforce.ide.core.model.OrgModel;
import com.salesforce.ide.core.project.ForceProjectException;
import com.salesforce.ide.core.remote.Connection;
import com.salesforce.ide.core.remote.ForceConnectionException;
import com.salesforce.ide.core.remote.ForceRemoteException;
import com.salesforce.ide.core.remote.InsufficientPermissionsException;
import com.salesforce.ide.core.remote.InvalidLoginException;
import com.salesforce.ide.core.remote.MetadataStubExt;
import com.salesforce.ide.core.remote.metadata.CustomObjectNameResolver;
import com.salesforce.ide.core.remote.metadata.DescribeMetadataResultExt;
import com.salesforce.ide.core.remote.metadata.FileMetadataExt;
import com.salesforce.ide.ui.internal.Messages;
import com.salesforce.ide.ui.widgets.MultiCheckboxButton;
import com.sforce.soap.metadata.FileProperties;
import com.sforce.soap.metadata.ListMetadataQuery;

/**
*
* @author ataylor
*/
public class PackageManifestController extends Controller {
    private static Logger logger = Logger.getLogger(PackageManifestController.class);

    public static final String STANDARD_OBJECT_NODE_NAME = "objects - standard"; //$NON-NLS-1$
    public static final String CUSTOM_OBJECT_NODE_NAME = "objects - custom"; //$NON-NLS-1$

    private static final String CACHE_TYPES = "types"; //$NON-NLS-1$
    private static final String CACHE_ISSUBTYPE = "isSubType"; //$NON-NLS-1$
    private static final String CACHE_PARENT = "parent"; //$NON-NLS-1$

    private final Map<String, PackageTreeNode> map = new HashMap<String, PackageTreeNode>();
    private PackageTreeNode root;
    private FileMetadataExt ext;
    private Connection connection;
    private Document cache;
    private Document manifestDoc;

    private final List<String> typeList = new ArrayList<String>();
    private final List<String> subTypes = new ArrayList<String>();
    private final Map<String, String> parentTypes = new HashMap<String, String>();

    public PackageManifestController() throws ForceProjectException {
        model = new OrgModel();
    }

    @Override
    public void dispose() {

    }

    @Override
    public void finish(IProgressMonitor monitor) throws Exception {

    }

    @Override
    public void init() throws ForceProjectException {

    }

    @Override
    public void setProject(IProject project) {
        // if project is null, cache exists in plugin repro
        URL oldLocation = Utils.getCacheUrl(getProject());
        File oldCache = new File(oldLocation.getFile());

        // set project so check below points to project cache location
        super.setProject(project);

        // clear the cache or move to project
        if (oldCache.exists()) {
            // move an old cache if there is one
            URL newLocation = Utils.getCacheUrl(getProject());

            File newCache = new File(newLocation.getFile());
            if (!newCache.exists()) {
                oldCache.renameTo(newCache);
            } else {
                if (logger.isDebugEnabled()) {
                    try {
                        logger.debug("Cleared manifest cache " + oldCache.toURL().toExternalForm()); //$NON-NLS-1$
                    } catch (MalformedURLException e) {}
                }
                oldCache.delete();
            }
        }
    }

    public void setFileMetadatExt(FileMetadataExt ext) {
        this.ext = ext;
    }

    public void setManifestDoc(Document doc) {
        manifestDoc = doc;
    }

    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    private Connection getConnection() throws ForceConnectionException, InsufficientPermissionsException {
        if (connection == null) {
            connection =
                    ContainerDelegate.getInstance().getFactoryLocator().getConnectionFactory().getConnection(
                        getProject());
        }

        return connection;
    }

    /**
     * load cache if not exist, then create PME tree model and init model state.
     */
    public void updateManifest() {

        map.clear();
        cache = Utils.loadDocument(Utils.getCacheUrl(getProject()));
        if (cache == null) {
            loadAll();
        }
        createModel();
        initModel();
    }

    public void clearModel() {
        root = null;
        if (ext != null) {
            ext.setFileProperties(null);
        }
        parentTypes.clear();
        typeList.clear();
        subTypes.clear();
    }

    public void clearCache() {
        cache = null;
        URL url = Utils.getCacheUrl(getProject());
        if (url != null) {
            File file = new File(url.getFile());
            if (logger.isDebugEnabled()) {
                try {
                    logger.debug("Cleared manifest cache " + file.toURL().toExternalForm()); //$NON-NLS-1$
                } catch (MalformedURLException e) {}
            }
            file.delete();

        }
    }

    Map<String, Throwable> erroneousComponentTypes = new HashMap<String, Throwable>();
    boolean exceptionOccurred = false;

    /**
     * construct a map retain <type, list of its subtype> either from component factory or from cache. ex. <workflow,
     * sub-component types are WorkflowAlert, WorkflowFieldUpdate,..>
     */
    private void constructTypeToSubTypeCompMap() {
        if (ext != null) {
            String types[];
            try {
                types =
                        ContainerDelegate.getInstance().getServiceLocator().getMetadataService()
                        .getEnabledComponentTypes(getConnection(), true);
                if (Utils.isNotEmpty(types)) {
                    typeList.addAll(Arrays.asList(types));
                } else {
                    logger.debug("Getting no enabled component type for org '"
                            + getConnection().getForceProject().getUserName() + "' on '"
                            + getConnection().getForceProject().getEndpointServer() + "'");
                }

                for (String key : ext.getFilePropertiesMap(typeList).keySet()) {
                    try {
                        fillTypeStructures(key);
                    } catch (Throwable th) {
                        erroneousComponentTypes.put(key, th);
                    }
                }

            }
            // if exception occurred when getting enabled component, rollback to
            // getting component type from cache.
            catch (InsufficientPermissionsException e) {
                exceptionOccurred = true;
                logger.debug(e);
            } catch (ForceConnectionException e) {
                exceptionOccurred = true;
                logger.debug(e);
            } catch (ForceRemoteException e) {
                exceptionOccurred = true;
                logger.debug(e);
            } catch (InterruptedException e) {
                exceptionOccurred = true;
                logger.debug(e);
            } catch (FactoryException e) {
                exceptionOccurred = true;
                logger.debug(e);
            }

        } else if (ext == null || exceptionOccurred) {
            List<Node> componentTypes = getComponentTypesFromCache(cache, false);
            for (Node componentType : componentTypes) {
                String key = PackageManifestDocumentUtils.getComponentName(componentType);
                try {
                    fillTypeStructures(key);
                } catch (Throwable th) {
                    erroneousComponentTypes.put(key, th);
                }
            }
        }

        logAndDisplayWarnMsgIfNeeded(erroneousComponentTypes);
    }

    private void fillTypeStructures(String key) throws FactoryException {
        if (!ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory().isRegisteredComponentType(key)) {
            return;
        }

        List<String> subCompType =
                ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory().getSubComponentTypes(key);

        if (Utils.isNotEmpty(subCompType)) {
            for (String subType : subCompType) {
                subTypes.add(subType);
                parentTypes.put(subType, key);
            }
        }
    }

    public void updateCache(final URL cacheUrl) throws InvocationTargetException, InterruptedException {
        final IProgressService service = PlatformUI.getWorkbench().getProgressService();
        service.run(false, false, new IRunnableWithProgress() {
            @Override
            public void run(final IProgressMonitor monitor) throws InvocationTargetException {
                Set<String> keySet = ext.getFilePropertiesMap(typeList).keySet();

                monitor.beginTask("Updating project cache...", Utils.isNotEmpty(keySet) ? keySet.size() + 4 : 4);
                monitor.worked(1);

                try {
                    updateCacheWork(cacheUrl, monitor);
                } catch (InterruptedException e) {
                    logger.warn("Operation cancelled: " + e.getMessage()); //$NON-NLS-1$
                } catch (Throwable e) {
                    throw new InvocationTargetException(e);
                } finally {
                    if (monitor != null) {
                        monitor.done();
                    }
                }
            }
        });
    }

    /**
     * Building PME cache DOM document then persist in packageCache.xml.
     *
     * @param cacheUrl
     * @param monitor
     * @throws ParserConfigurationException
     * @throws TransformerException
     * @throws IOException
     * @throws FactoryException
     * @throws ForceConnectionException
     * @throws ForceRemoteException
     * @throws InterruptedException
     */
    protected void updateCacheWork(URL cacheUrl, IProgressMonitor monitor) throws ParserConfigurationException,
    TransformerException, IOException, FactoryException, ForceConnectionException, ForceRemoteException,
    InterruptedException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        cache = builder.newDocument();
        monitor.worked(1);

        Node root = cache.createElementNS(Constants.PACKAGE_MANIFEST_NAMESPACE_URI, Constants.PACKAGE_MANIFEST_TYPES);
        cache.appendChild(root);

        constructTypeToSubTypeCompMap();
        monitor.worked(1);

        Set<String> keySet = ext.getFilePropertiesMap(typeList).keySet();
        String[] keys = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keys);

        // query describe metadata for organization namespace - don't get from
        // ForceProject due to project might not be created yet.
        MetadataStubExt metadataStubExt =
                ContainerDelegate.getInstance().getFactoryLocator().getMetadataFactory().getMetadataStubExt(
                    getConnection());
        DescribeMetadataResultExt describeMetadataResultExt =
                ContainerDelegate.getInstance().getServiceLocator().getMetadataService().getDescribeMetadata(
                    metadataStubExt, new NullProgressMonitor());
        String organizationNamespace = describeMetadataResultExt.getOrganizationNamespace();

        for (String key : keys) {
            if (Constants.ABSTRACT_SHARING_RULE_TYPES.contains(key) || Constants.RULE_TYPES.contains(key)) {
                continue;
            }

            Element type =
                    cache.createElementNS(Constants.PACKAGE_MANIFEST_NAMESPACE_URI, Constants.PACKAGE_MANIFEST_TYPES);
            root.appendChild(type);

            if (subTypes.contains(key)) {
                type.setAttribute(CACHE_ISSUBTYPE, Boolean.TRUE.toString());
                type.setAttribute(CACHE_PARENT, parentTypes.get(key));
            } else {
                type.setAttribute(CACHE_ISSUBTYPE, Boolean.FALSE.toString());
            }

            Node name =
                    cache.createElementNS(Constants.PACKAGE_MANIFEST_NAMESPACE_URI,
                        Constants.PACKAGE_MANIFEST_TYPE_NAME);
            type.appendChild(name);
            name.setTextContent(key);

            Component component =
                    ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory()
                    .getComponentByComponentType(key);
            if (component != null && component.isWithinFolder()) {
                ListMetadataQuery folderQuery = new ListMetadataQuery();
                folderQuery.setType(component.getFolderNameIfFolderTypeMdComponent());
                FileMetadataExt ext =
                        ContainerDelegate.getInstance().getServiceLocator().getMetadataService().listMetadata(
                            connection, new ListMetadataQuery[] { folderQuery }, new NullProgressMonitor());

                if (ext != null && Utils.isNotEmpty(ext.getFileProperties())) {
                    for (FileProperties file : Utils
                            .removePackagedFiles(ext.getFileProperties(), organizationNamespace)) {
                        Node members =
                                cache.createElementNS(Constants.PACKAGE_MANIFEST_NAMESPACE_URI,
                                    Constants.PACKAGE_MANIFEST_TYPE_MEMBERS);
                        type.appendChild(members);
                        members.setTextContent(file.getFullName());
                    }
                }
            }

            List<FileProperties> files = ext.getFilePropertiesMap(typeList).get(key);
            if (Utils.isEmpty(files)) {
                continue;
            }

            Collections.sort(files, new Comparator<FileProperties>() {
                @Override
                public int compare(FileProperties o1, FileProperties o2) {
                    // To guard against component names being returned from the server as 'null'
                    // W-1150256
                    if (o1.getFullName() == null || o2.getFullName() == null) {
                        return 0;
                    }
                    return String.CASE_INSENSITIVE_ORDER.compare(o1.getFullName(), o2.getFullName());
                }
            });

            for (FileProperties file : Utils.removePackagedFiles(files.toArray(new FileProperties[files.size()]),
                organizationNamespace)) {
                Node members =
                        cache.createElementNS(Constants.PACKAGE_MANIFEST_NAMESPACE_URI,
                            Constants.PACKAGE_MANIFEST_TYPE_MEMBERS);
                type.appendChild(members);
                members.setTextContent(file.getFullName());
            }

            monitor.worked(1);
        }

        Utils.saveDocument(cache, cacheUrl.getPath());
    }

    /**
     * get top level component type tree nodes, ex. applications, classes, reports, etc.
     *
     * @return array of componet type tree nodes.
     */
    public Object[] getEnabledCompTypeTreeNodes() {
        if (root == null) {
            updateManifest();
        }

        return root != null ? root.getChildren() : new Object[0];
    }

    public Map<String, PackageTreeNode> getPackageTreeNodeMap() {
        return map;
    }

    public PackageTreeNode getNode(String name) {
        return map.get(name.toLowerCase());
    }

    public String getPath(PackageTreeNode node) {
        StringBuilder builder = new StringBuilder();
        Stack<String> stack = new Stack<String>();
        while (node != root) {
            String path = node.getName();
            if (node instanceof ComponentTypeNode) {
                path = ((ComponentTypeNode) node).getComponent().getComponentType();
            }
            stack.push(path.toLowerCase());
            node = (PackageTreeNode) node.getParent();
        }

        while (!stack.isEmpty()) {
            builder.append(stack.pop());
            builder.append(Constants.FOWARD_SLASH);
        }

        return builder.toString();
    }

    public String getPathForComponentType(String componentType) throws FactoryException {
        return getComponentTypeName(ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory()
            .getComponentByComponentType(componentType));
    }

    // load file metadata from server
    public void loadAll() {
        try {
            // retrieve all metadata or is subset needs to be augmented
            if (ext == null || ext.getFileProperties() == null || ext.isSubset()) {
                loadFileMetadata();
            }

            updateCache(Utils.getCacheUrl(getProject()));
        } catch (InterruptedException e) {
            logger.warn("Operation cancelled: " + e.getMessage()); //$NON-NLS-1$
        } catch (InvocationTargetException e) {
            if (e.getTargetException() instanceof InsufficientPermissionsException) {
                DialogUtils.getInstance().presentInsufficientPermissionsDialog(
                    (InsufficientPermissionsException) e
                    .getTargetException());
            } else if (e.getTargetException() instanceof InvalidLoginException) {
                // log failure
                logger.warn("Unable to refresh file metadata: " //$NON-NLS-1$
                    + ForceExceptionUtils.getRootCauseMessage(e.getTargetException()));
                // choose further project create direction
                DialogUtils.getInstance().invalidLoginDialog(
                    ForceExceptionUtils.getRootCauseMessage(e.getTargetException()));
            } else {
                logger.error("Unable to refresh file metadata", ForceExceptionUtils //$NON-NLS-1$
                    .getRootCause(e.getTargetException()));
                StringBuffer strBuff = new StringBuffer();
                strBuff.append("Unable to refresh file metadata:\n\n").append(
                    ForceExceptionUtils.getStrippedRootCauseMessage(e)).append("\n\n ");
                Utils.openError("Refresh Error", strBuff.toString());
            }
        } catch (Exception e) {
            logger.error("Unable to refresh file metadata", ForceExceptionUtils.getRootCause(e)); //$NON-NLS-1$
            StringBuffer strBuff = new StringBuffer();
            strBuff.append("Unable to refresh file metadata:\n\n").append(
                ForceExceptionUtils.getStrippedRootCauseMessage(e)).append("\n\n ");
            Utils.openError("Refresh Error", strBuff.toString());
        }
    }

    private void loadFileMetadata() throws InvocationTargetException, InterruptedException {
        final IProgressService service = PlatformUI.getWorkbench().getProgressService();
        service.run(false, false, new IRunnableWithProgress() {
            @Override
            public void run(final IProgressMonitor monitor) throws InvocationTargetException {
                monitor.beginTask("Fetching organization details...", 3);
                monitor.worked(1);

                try {
                    monitor.subTask("Preparing to fetch component metadata...");

                    // formulate queries: either just a subset to augment passed
                    // file properties or a full query
                    ListMetadataQuery[] listMetadataQueries = null;
                    if (ext != null && ext.isSubset()) {
                        listMetadataQueries =
                                ContainerDelegate.getInstance().getServiceLocator().getMetadataService()
                                .getListMetadataQueryArray(getConnection(), ext.getComponentTypes(), true,
                                    monitor);
                    } else {
                        listMetadataQueries =
                                ContainerDelegate.getInstance().getServiceLocator().getMetadataService()
                                .getListMetadataQueryArray(getConnection(), true, monitor);
                    }
                    monitor.worked(1);

                    if (listMetadataQueries == null) {
                        logger.warn("ListMetadataQuery for org is empty"); //$NON-NLS-1$
                        return;
                    }

                    // use queries count to determine tick length
                    monitor.beginTask("Fetching component metadata...", Utils.isNotEmpty(listMetadataQueries)
                        ? listMetadataQueries.length + 1 : IProgressMonitor.UNKNOWN);
                    monitor.worked(1);

                    // perform list metadata request w/ queries
                    FileMetadataExt tmpFileMetadataExt =
                            ContainerDelegate.getInstance().getServiceLocator().getMetadataService().listMetadata(
                                getConnection(), listMetadataQueries, monitor);

                    // query describe metadata for organization namespace -
                    // don't get from ForceProject due to project might not be
                    // created yet.
                    MetadataStubExt metadataStubExt =
                            ContainerDelegate.getInstance().getFactoryLocator().getMetadataFactory()
                            .getMetadataStubExt(getConnection());
                    DescribeMetadataResultExt describeMetadataResultExt =
                            ContainerDelegate.getInstance().getServiceLocator().getMetadataService()
                            .getDescribeMetadata(metadataStubExt, new NullProgressMonitor());
                    String organizationNamespace = describeMetadataResultExt.getOrganizationNamespace();

                    // use client provided file properties or augment with fill list
                    // filter out packaged fileproperties from returned
                    // fileproperties (pme should not show packaged content)
                    FileProperties[] removedPackagedFileProps =
                            Utils.removePackagedFiles(tmpFileMetadataExt.getFileProperties(), organizationNamespace);

                    if (null == ext) {
                        ext = tmpFileMetadataExt;
                        ext.setFileProperties(removedPackagedFileProps);
                    } else if (ext.getFileProperties() == null) {
                        ext.setFileProperties(removedPackagedFileProps);

                    } else if (ext != null && ext.isSubset()) {
                        ext.addFileProperties(removedPackagedFileProps);
                        ext.setSubset(false);

                    }

                } catch (InterruptedException e) {
                    logger.warn("Operation cancelled: " + e.getMessage()); //$NON-NLS-1$
                } catch (Throwable e) {
                    throw new InvocationTargetException(e);
                } finally {
                    if (monitor != null) {
                        monitor.done();
                    }
                }
            }
        });
    }

    // TODO consolidate with ComponentTypeNode.getName()
    String getComponentTypeName(Component component) {
        return component.getComponentType().toLowerCase();
    }

    /**
     * construct a map retain <type, list of its subtype> & set state on node (checked? / isWildcardSupported? )
     */
    private void initModel() {
        constructTypeToSubTypeCompMap();
        List<Node> componentTypes = PackageManifestDocumentUtils.getComponentTypes(manifestDoc);

        Map<String, Throwable> erroneousComponentTypes = new HashMap<String, Throwable>();
        for (Node componentType : componentTypes) {
            String componentTypeName = PackageManifestDocumentUtils.getComponentName(componentType);

            try {
                if (subTypes.contains(componentTypeName)) {
                    String parentComponentName = parentTypes.get(componentTypeName);
                    Component parentComponent =
                            ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory()
                            .getComponentByComponentType(parentComponentName);

                    String parentPath = getComponentTypeName(parentComponent);
                    List<Node> members = PackageManifestDocumentUtils.getComponentMembers(componentType);

                    for (Node member : members) {
                        String memberName = PackageManifestDocumentUtils.getMemberName(member);

                        int index = memberName.indexOf(Constants.DOT);
                        String parentName = memberName.substring(0, index);
                        String subComponentName = memberName.substring(index + 1);

                        StringBuilder path = null;

                        if (CustomObjectNameResolver.getCheckerForCustomObject().check(parentName,
                            Constants.CUSTOM_OBJECT)) {
                            path = new StringBuilder(Constants.CUSTOM_OBJECT);
                            path.append(Constants.FOWARD_SLASH);
                        } else {
                            path = new StringBuilder(parentPath);
                            path.append(Constants.FOWARD_SLASH);
                        }

                        path.append(parentName).append(Constants.FOWARD_SLASH).append(componentTypeName).append(
                            Constants.FOWARD_SLASH).append(subComponentName).append(Constants.FOWARD_SLASH);

                        PackageTreeNode memberTreeNode = getNode(path.toString());

                        if (memberTreeNode != null) {
                            memberTreeNode.setState(MultiCheckboxButton.getBlackCheckedState());
                        }
                    }
                } else {

                    List<Node> members = PackageManifestDocumentUtils.getComponentMembers(componentType);
                    for (Node member : members) {
                        StringBuilder path = new StringBuilder();
                        if (componentTypeName.equals(Constants.CUSTOM_OBJECT)) {
                            path.append(Constants.CUSTOM_OBJECT);
                        }

                        else {
                            path.append(componentTypeName);
                        }
                        path.append(Constants.FOWARD_SLASH);
                        String memberName = PackageManifestDocumentUtils.getMemberName(member);

                        if (path.toString().startsWith(Constants.CUSTOM_OBJECT + Constants.FOWARD_SLASH)
                                && !memberName.equals(Constants.PACKAGE_MANIFEST_WILDCARD)
                                && CustomObjectNameResolver.getCheckerForStandardObject().check(memberName,
                                    Constants.CUSTOM_OBJECT)) {
                            path.delete(0, path.length());
                            path.append(Constants.STANDARD_OBJECT);
                            path.append(Constants.FOWARD_SLASH);
                        }

                        PackageTreeNode memberTreeNode = getNode(path + memberName + Constants.FOWARD_SLASH);

                        if (memberTreeNode != null) {
                            memberTreeNode.setState(MultiCheckboxButton.getBlackCheckedState());
                        }

                        else if (memberName.equals(Constants.PACKAGE_MANIFEST_WILDCARD)) {
                            if (path.toString().startsWith(Constants.STANDARD_OBJECT)) {
                                path.delete(0, path.length());
                                path.append(Constants.CUSTOM_OBJECT);
                                path.append(Constants.FOWARD_SLASH);
                            }
                            getNode(path.toString()).wildcardSelected = true;
                        }
                    }
                }

            } catch (Throwable th) {
                erroneousComponentTypes.put(componentTypeName, th);
            }
        }

        for (PackageTreeNode node : root.getChildList()) {
            try {
                boolean hasWildCard = node.isWildcardSelected();
                for (PackageTreeNode componentChild : node.getChildList()) {
                    for (PackageTreeNode customObjectChild : componentChild.getChildList()) {
                        for (PackageTreeNode customObjectFolder : customObjectChild.getChildList()) {
                            initParentState(customObjectFolder, hasWildCard);
                        }

                        initParentState(customObjectChild, hasWildCard);
                    }

                    initChildState(componentChild, hasWildCard);
                    initParentState(componentChild, hasWildCard);
                }

                initParentState(node, hasWildCard);
            } catch (Throwable th) {
                erroneousComponentTypes.put(node.getName(), th);
            }
        }

        logAndDisplayWarnMsgIfNeeded(erroneousComponentTypes);
    }

    void logAndDisplayWarnMsgIfNeeded(Map<String, Throwable> erroneousComponentTypes) {
        if (Utils.isNotEmpty(erroneousComponentTypes)) {
            StringBuilder warningDialogMsg = new StringBuilder();
            StringBuilder logMsg = new StringBuilder();
            warningDialogMsg.append("Exception happened when resolving component type(s), "
                    + "so no component will be added to package manifest editor for these types. \n");
            logMsg.append("Exception happened when PME creating model for: \n");
            for (String compName : erroneousComponentTypes.keySet()) {
                warningDialogMsg.append("* ").append(compName).append("\n");
                Throwable throwable = erroneousComponentTypes.get(compName);
                logMsg.append("* Component type '").append(compName).append("' ");

                if (Utils.isNotEmpty(throwable)) {
                    logger.debug("Exception happened for component type '" + compName + "' in PME.", throwable);

                } else {
                    logMsg
                    .append("No exception occurred but the component factory is unable to resolve component type '"
                            + compName + "'");
                }

            }
            warningDialogMsg.append("See log for detail exception messages.");
            Utils.openWarn(Messages.PackageManifest_content_Warning_text, warningDialogMsg.toString());
        }
    }

    private void initChildState(PackageTreeNode treeNode, boolean hasWildCard) {
        if (treeNode instanceof CustomObjectTypeNode && treeNode.hasChildren()) {
            if (MultiCheckboxButton.isBlackChecked(treeNode.getState())) {
                initCustomObjectChildState(treeNode, MultiCheckboxButton.getBlackCheckedState());
            }
        }
    }

    private void initCustomObjectChildState(PackageTreeNode node, int state) {
        for (PackageTreeNode child : node.getChildList()) {
            initCustomObjectChildState(child, state);
        }

        node.setState(state);
    }

    private void initParentState(PackageTreeNode treeNode, boolean hasWildCard) {
        if (hasWildCard) {
            treeNode.setState(MultiCheckboxButton.getBlackCheckedState());
        } else {
            if (treeNode instanceof CustomObjectTypeNode && treeNode.hasChildren() && allChildChecked(treeNode)) {
                String componentName = ((ComponentTypeNode) treeNode.getParent()).getComponent().getComponentType();

                if (componentName.equals(Constants.STANDARD_OBJECT)) {
                    componentName = Constants.CUSTOM_OBJECT;
                }

                Node componentNode = PackageManifestDocumentUtils.getComponentNode(manifestDoc, componentName);
                if (PackageManifestDocumentUtils.getMemberNode(componentNode, treeNode.getName()) == null) {
                    treeNode.setState(MultiCheckboxButton.getSchroedingerState());
                }

                else {
                    treeNode.setState(MultiCheckboxButton.getBlackCheckedState());
                }
            } else if (MultiCheckboxButton.isBlackChecked(treeNode.getState()) || allChildChecked(treeNode)) {
                treeNode.setState(MultiCheckboxButton.getBlackCheckedState());
            } else if (anyChildChecked(treeNode) || anyChildSchroedinger(treeNode)) {
                treeNode.setState(MultiCheckboxButton.getSchroedingerState());
            }
        }
    }

    protected boolean anyChildSchroedinger(PackageTreeNode node) {
        for (PackageTreeNode child : node.getChildList()) {
            if (MultiCheckboxButton.isSchroedinger(child.getState())) {
                return true;
            }
        }

        return false;
    }

    protected boolean anyChildChecked(PackageTreeNode node) {
        for (PackageTreeNode child : node.getChildList()) {
            if (MultiCheckboxButton.isBlackChecked(child.getState())) {
                return true;
            }
        }

        return false;
    }

    protected boolean allChildChecked(PackageTreeNode node) {
        boolean flag = node.getChildren().length > 0;
        for (PackageTreeNode child : node.getChildList()) {
            if (!MultiCheckboxButton.isBlackChecked(child.getState())) {
                return false;
            }
        }

        return flag;
    }

    /**
     * Create PME tree model nodes - adding component to component type, sub-component to component, and remove parent
     * component when there is no children and not support wildcard for standard object.
     */
    private void createModel() {
        root = new PackageTreeNode(null);
        List<Node> componentTypes = getComponentTypesFromCache(cache, false);

        Map<String, Throwable> erroneousComponentTypes = new HashMap<String, Throwable>();
        for (Node componentType : componentTypes) {
            Component comp = null;
            try {
                String compName = PackageManifestDocumentUtils.getComponentName(componentType);
                comp =
                        ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory()
                        .getComponentByComponentType(compName);

                if (!Utils.isEmpty(comp)) {
                    if (comp.getComponentType().equals(Constants.STANDARD_OBJECT)) {
                        comp.setDefaultFolder(STANDARD_OBJECT_NODE_NAME);
                        comp.setWildCardSupported(false);
                    } else if (comp.getComponentType().equals(Constants.CUSTOM_OBJECT)) {
                        comp.setDefaultFolder(CUSTOM_OBJECT_NODE_NAME);
                    }

                    ComponentTypeNode node = new ComponentTypeNode(comp);
                    node.retrieved = true;
                    root.addChild(node);
                    map.put(getPath(node), node);

                    addComponents(componentType, node);
                    addSubComponents(componentType, node);
                    removeUnneededComponents(componentType, node);
                } else {
                    erroneousComponentTypes.put(PackageManifestDocumentUtils.getComponentName(componentType), null);

                }

            } catch (Throwable e) {
                erroneousComponentTypes.put(PackageManifestDocumentUtils.getComponentName(componentType), e);
            }
        }

        logAndDisplayWarnMsgIfNeeded(erroneousComponentTypes);

    }

    private void addComponents(Node node, ComponentTypeNode parentNode) {
        List<Node> members = PackageManifestDocumentUtils.getComponentMembers(node);
        for (Node subComponentMember : members) {
            String memberName = PackageManifestDocumentUtils.getMemberName(subComponentMember);

            if (memberName == null) {
                continue;
            }

            if (parentNode.getComponent().isWithinFolder()) {
                addFolderComponent(memberName, parentNode);
            } else {
                PackageTreeNode componentNode = null;
                if (parentNode.getComponent().hasSubComponentTypes()
                        || parentNode.getComponent().getComponentType().equals(Constants.STANDARD_OBJECT)) {
                    componentNode = new CustomObjectTypeNode(memberName);
                } else {
                    componentNode = new ComponentNode(memberName);
                }
                componentNode.retrieved = true;
                parentNode.addChild(componentNode);
                map.put(getPath(componentNode), componentNode);
            }
        }
    }

    private void addFolderComponent(String memberName, ComponentTypeNode parentNode) {
        int index = memberName.indexOf(Constants.FOWARD_SLASH);

        if (index == -1) {
            PackageTreeNode folderNode = new ComponentFolderNode(memberName);
            parentNode.addChild(folderNode);
            parentNode.retrieved = true;
            map.put(getPath(folderNode), folderNode);
        }

        else {
            String parentName = memberName.substring(0, index);
            String compName = memberName.substring(index + 1);

            String path = getPath(parentNode);
            path += parentName.toLowerCase() + Constants.FOWARD_SLASH;

            PackageTreeNode folderNode = map.get(path);
            // create folder node if it doesn't exist: scenario where folder is
            // managed installed (will be removed from packageCache.xml) but
            // component in folder is not.
            if (Utils.isEmpty(folderNode)) {
                folderNode = new ComponentFolderNode(parentName);
                parentNode.addChild(folderNode);
                parentNode.retrieved = true;
                map.put(getPath(folderNode), folderNode);

            }
            PackageTreeNode componentNode = new ComponentNode(compName);
            folderNode.addChild(componentNode);
            componentNode.retrieved = true;
            map.put(getPath(componentNode), componentNode);
        }
    }

    private void addSubComponents(Node node, ComponentTypeNode parentNode) {
        String parentType = parentNode.getComponent().getComponentType();

        List<Node> subComponentTypes = getSubComponentTypes(cache, parentType.equals(Constants.STANDARD_OBJECT) ? Constants.CUSTOM_OBJECT :  parentType);
        for (Node subComponentType : subComponentTypes) {
            String subName = PackageManifestDocumentUtils.getComponentName(subComponentType);

            List<Node> members = PackageManifestDocumentUtils.getComponentMembers(subComponentType);
            for (Node subComponentMember : members) {
                String memberName = PackageManifestDocumentUtils.getMemberName(subComponentMember);

                String parentName = null;
                String compName = null;
                int index = memberName.indexOf('.');
                if (index == -1) {
                    parentName = parentNode.getChildList().get(0).getName();
                    compName = memberName;
                } else {
                    parentName = memberName.substring(0, index);
                    compName = memberName.substring(index + 1);
                }

                String path = getPath(parentNode);
                if (CustomObjectNameResolver.getCheckerForCustomObject().check(parentName, Constants.CUSTOM_OBJECT)) {
                    path = Constants.CUSTOM_OBJECT.toLowerCase() + Constants.FOWARD_SLASH;
                }

                path += parentName.toLowerCase() + Constants.FOWARD_SLASH;

                PackageTreeNode componentFolder = map.get(path);

                if (componentFolder != null) {
                    PackageTreeNode customObjectFolderNode =
                            map.get(path + subName.toLowerCase() + Constants.FOWARD_SLASH);

                    if (customObjectFolderNode == null) {
                        if (index != -1) {
                            customObjectFolderNode = new CustomObjectFolderNode(subName);
                            componentFolder.addChild(customObjectFolderNode);
                            map.put(getPath(customObjectFolderNode), customObjectFolderNode);
                        } else {
                            customObjectFolderNode = componentFolder;
                        }
                    }

                    CustomObjectComponentNode componentNode = new CustomObjectComponentNode(compName);

                    try {
                        if (ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory()
                                .isRegisteredComponentType(subName)) {
                            componentNode.setComponent(ContainerDelegate.getInstance().getFactoryLocator()
                                .getComponentFactory().getComponentByComponentType(subName));
                        }
                    } catch (FactoryException e) {
                        logger.warn("Unable to aquire subtype " + subName + " from component factory", e); //$NON-NLS-1$  //$NON-NLS-2$
                    }

                    customObjectFolderNode.addChild(componentNode);
                    map.put(getPath(componentNode), componentNode);
                }
            }
        }
    }

    private void removeUnneededComponents(Node node, ComponentTypeNode parentNode) {
        if (!parentNode.getComponent().isWildCardSupported() && !parentNode.hasChildren()) {
            root.getChildList().remove(parentNode);
        }
    }

    public static List<Node> getComponentTypesFromCache(Document doc, boolean isSubType) {
        List<Node> list = new ArrayList<Node>();
        Node packageNode = PackageManifestDocumentUtils.getPackageNode(doc);

        if (packageNode != null) {
            NodeList nodeList = packageNode.getChildNodes();
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node item = nodeList.item(i);

                if (item.getNodeName().equals(CACHE_TYPES) &&
                        item.getAttributes().getNamedItem(CACHE_ISSUBTYPE).getNodeValue().equals(("" + isSubType).toLowerCase())) { //$NON-NLS-1$
                    list.add(item);
                }
            }
        }

        return list;
    }

    public static List<Node> getSubComponentTypes(Document doc, String parent) {
        List<Node> componentTypes = getComponentTypesFromCache(doc, true);
        List<Node> retList = new ArrayList<Node>(componentTypes);
        for (Node componentType : componentTypes) {
            if (!componentType.getAttributes().getNamedItem(CACHE_PARENT).getNodeValue().equals(parent)) {
                retList.remove(componentType);
            }
        }

        return retList;
    }

    public PackageTreeNode getRoot() {
        return root;
    }

}
TOP

Related Classes of com.salesforce.ide.ui.packagemanifest.PackageManifestController

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.