/*******************************************************************************
* 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.core.factories;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import org.apache.log4j.Logger;
import org.apache.xerces.parsers.DOMParser;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.salesforce.ide.api.metadata.types.Package;
import com.salesforce.ide.api.metadata.types.PackageTypeMembers;
import com.salesforce.ide.core.internal.utils.Constants;
import com.salesforce.ide.core.internal.utils.Utils;
import com.salesforce.ide.core.model.Component;
import com.salesforce.ide.core.model.ComponentList;
import com.salesforce.ide.core.model.ProjectPackage;
import com.salesforce.ide.core.model.ProjectPackageList;
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.ForceException;
import com.salesforce.ide.core.remote.ForceRemoteException;
import com.salesforce.ide.core.services.RetrieveException;
import com.sforce.soap.metadata.RetrieveRequest;
/**
* Encapsulates functionality related managing metadata package manifests.
*
* @author cwall
*/
public class PackageManifestFactory extends BaseFactory {
private static final Logger logger = Logger.getLogger(PackageManifestFactory.class);
private List<String> defaultDisabledRetrieveComponentTypes = null;
public List<String> getDefaultDisabledRetrieveComponentTypes() {
return defaultDisabledRetrieveComponentTypes;
}
public void setDefaultDisabledRetrieveComponentTypes(List<String> defaultDisabledRetrieveComponentTypes) {
this.defaultDisabledRetrieveComponentTypes = defaultDisabledRetrieveComponentTypes;
}
public Component getPackageManifestComponentInstance() throws FactoryException {
return getComponentFactory().getComponentByComponentType(Constants.PACKAGE_MANIFEST);
}
/**
* Generate a default manifest with registered component types.
*
* @return
* @throws FactoryException
*/
public Package getDefaultPackageManifest() throws FactoryException {
return createDefaultPackageManifest();
}
/**
* Parse project's default manifest file creating API package manifest.
*
* @param project
* @return
* @throws FactoryException
*/
public Package getDefaultPackageManifest(IProject project) throws FactoryException {
return getPackageManifest(project, Constants.DEFAULT_PACKAGED_NAME);
}
public Package getPackageManifest(IProject project) throws FactoryException {
if (project == null) {
throw new IllegalArgumentException("Project and/or package name cannot be null");
}
if (logger.isDebugEnabled()) {
logger.debug("Getting manifest in project '" + project.getName() + "'");
}
// find manifest file for package
IFile manfiestFile = getPackageManifestFile(project);
// generate package object by parsing package file
Package packageManifest = null;
if (manfiestFile != null && manfiestFile.exists()) {
if (logger.isDebugEnabled()) {
logger.debug("Found existing existing package manifest.");
}
try {
packageManifest = parsePackageManifest(manfiestFile.getRawLocation().toFile());
} catch (Exception e) {
logger.error("Unable to create package manifest", e);
throw new FactoryException(e);
}
}
return packageManifest;
}
/**
* Create API package manifest for a given package name in a project. If manifest file exists, parse into API
* package manifest, otherwise generate a default manifest with registered component types.
*
* @param project
* @param packageName
* @return
* @throws FactoryException
* @throws FactoryException
*/
public Package getPackageManifest(IProject project, String packageName) throws FactoryException {
if (Utils.isEmpty(packageName) || project == null) {
throw new IllegalArgumentException("Project and/or package name cannot be null");
}
if (logger.isDebugEnabled()) {
logger.debug("Getting manifest for package '" + packageName + "' in project '" + project.getName() + "'");
}
// find package manifest in project
Package packageManifest = getPackageManifest(project);
if (logger.isDebugEnabled()) {
logger.debug("Could not find existing package manifest. Creating default package manifest.");
}
// not found in project, create one on fly
if (packageManifest == null) {
try {
Connection connection = getConnectionFactory().getConnection(project);
packageManifest = getDefaultPackageManifest(connection);
} catch (Exception e) {
logger.warn("Unable to create manifest from permissible object types. Creating default manifest.");
packageManifest = createDefaultPackageManifest();
}
}
return packageManifest;
}
public String getPackageNameFromPackageManifest(IProject project) throws FactoryException {
if (project == null) {
throw new IllegalArgumentException("Project cannot be null");
}
Package packageManifest = getPackageManifest(project);
return packageManifest != null ? packageManifest.getFullName() : null;
}
public void setDefaultPackageManifest(Connection connection, RetrieveRequest retrieveRequest)
throws ForceConnectionException, ForceRemoteException, InterruptedException {
Package packageManifest = getDefaultPackageManifest(connection);
retrieveRequest.setUnpackaged(convert(packageManifest));
}
public Package getDefaultPackageManifest(Connection connection) throws ForceConnectionException,
ForceRemoteException, InterruptedException {
return createPackageManifest(connection, Constants.DEFAULT_PACKAGED_NAME);
}
public Package getDefaultPackageManifest(ProjectPackageList projectPackageList) throws RetrieveException {
if (projectPackageList == null) {
throw new IllegalArgumentException("Project package cannot be null");
}
Package defaultPackageManifest = null;
ProjectPackage defaultPackageProjectPackage = projectPackageList.getDefaultPackageProjectPackage();
try {
if (defaultPackageProjectPackage.getPackageManifest() != null
&& defaultPackageProjectPackage.hasPackageManifest()) {
IFile packageManifestFile = defaultPackageProjectPackage.getPackageManifest().getFileResource();
if (packageManifestFile.exists()) {
defaultPackageManifest = getPackageManifestFromFile(packageManifestFile);
} else {
defaultPackageManifest = getDefaultPackageManifest();
}
} else if (projectPackageList.getProject() != null) {
defaultPackageManifest = getDefaultPackageManifest(projectPackageList.getProject());
} else if (projectPackageList.getProject() == null) {
defaultPackageManifest = getDefaultPackageManifest();
}
} catch (Exception e) {
logger.error("Unable to create default package manifest", e);
throw new RetrieveException("Unable to create default package manifest", e);
}
return defaultPackageManifest;
}
/**
* @param project
* @param packageName
* @return
* @throws FactoryException
*/
public Component getPackageManifestComponent(IProject project) throws FactoryException {
IFile manfiestFile = getPackageManifestFile(project);
return getComponentFactory().getComponentFromFile(manfiestFile, true);
}
public IFile getPackageManifestFile(IProject project) {
if (project == null) {
throw new IllegalArgumentException("Project cannot be null");
}
if (logger.isDebugEnabled()) {
logger.debug("Getting manifest file in project '" + project.getName() + "'");
}
// find manifest file for package
IFile manfiestFile = project.getFile("src/" + Constants.PACKAGE_MANIFEST_FILE_NAME);
if (manfiestFile == null) {
if (logger.isDebugEnabled()) {
logger
.debug(Constants.PACKAGE_MANIFEST_FILE_NAME + " not found in project '" + project.getName()
+ "'");
}
return null;
}
if (logger.isDebugEnabled()) {
logger.debug("Filepath for manifest is '" + manfiestFile.getRawLocation().toOSString() + "'");
}
return manfiestFile;
}
public Document getPackageManifestDOMDocument(IProject project) throws FactoryException {
IFile packageManifestFile = getPackageManifestFile(project);
Document document = null;
try {
InputSource content = new InputSource(packageManifestFile.getContents());
DOMParser parser = new DOMParser();
parser.parse(content);
document = parser.getDocument();
} catch (CoreException ce) {
String logMessage = Utils.generateCoreExceptionLog(ce);
logger.error("Core exception occurred when marshalling package manifest into DOM document: " + logMessage,
ce);
throw new FactoryException("Unable to create DOM document from path "
+ packageManifestFile.getProjectRelativePath().toPortableString(), ce);
} catch (SAXException se) {
logger.error("SAX exception occurred when marshalling package manifest into DOM document", se);
throw new FactoryException("Unable to create DOM document from path "
+ packageManifestFile.getProjectRelativePath().toPortableString(), se);
} catch (IOException ioe) {
logger.error("IO exception occurred when marshalling package manifest into DOM document", ioe);
throw new FactoryException("Unable to create DOM document from path "
+ packageManifestFile.getProjectRelativePath().toPortableString(), ioe);
}
return document;
}
public Package createPackageManifest(String packageName) {
Package packageManifest = createGenericDefaultPackageManifest();
if (Utils.isNotEmpty(packageName) && !Constants.DEFAULT_PACKAGED_NAME.equals(packageName)) {
packageManifest.setFullName(packageName);
}
return packageManifest;
}
/**
* @param componentTypes
* @return
*/
public Package createDefaultPackageManifestForComponentTypes(String[] componentTypes) {
return createPackageManifestForComponentTypes(Constants.DEFAULT_PACKAGED_NAME, componentTypes);
}
/**
* @param packageName
* @param componentTypes
* @return
*/
public Package createPackageManifestForComponentTypes(String packageName, String[] componentTypes) {
if (Utils.isEmpty(packageName) || Utils.isEmpty(componentTypes)) {
throw new IllegalArgumentException("Package name and/or object types cannot be null");
}
Package packageManifest = createGenericDefaultPackageManifest();
for (String componentType : componentTypes) {
if (!getComponentFactory().isWildCardSupportedComponentType(componentType)
|| !isEnabledRetrieveComponentType(componentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping disabled object type '" + componentType + "'");
}
continue;
}
PackageTypeMembers packageTypeMember = createPackageTypeMembers(componentType, new String[] { "*" });
packageManifest.getTypes().add(packageTypeMember);
}
if (Utils.isNotEmpty(packageName) && !Constants.DEFAULT_PACKAGED_NAME.equals(packageName)) {
packageManifest.setFullName(packageName);
}
return packageManifest;
}
/**
* This method is only for the deployment code, which has to be careful about custom objects and their contents.
*
* See W-837427 As should be obvious, this method is a quick fix that should be refactored.
*
* @return the package manifest to be deployed
*/
public Package createSpecialDefaultPackageManifest() {
Package packageManifest = new Package();
// Only "wildcard-supported" component types appear in the deployment
// manifest, according to the code.
//
// However, custom object components have subcomponents which can be
// deployed without
// their parent custom objects. These subcomponents are found in .object
// files.
// The deployment code can only write a .object file to the deployment
// package; it can't
// write only the subcomponent. To prevent the whole custom object from
// being deployed
// we must remove the "*" from the deployment manifest. If the object
// really is being
// deployed, its name will appear in the manifest. See changelist
// 1452239.
//
List<String> componentTypes = getComponentFactory().getWildcardSupportedComponentTypes();
for (String componentType : componentTypes) {
if (!isEnabledRetrieveComponentType(componentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping disabled object type '" + componentType + "' from deployment");
}
continue;
}
PackageTypeMembers packageTypeMember = null;
if (componentType.equals("CustomObject")) {
packageTypeMember = createPackageTypeMembers(componentType, new String[0]);
} else if (!componentType.equals("Settings")){
packageTypeMember = createPackageTypeMembers(componentType, new String[] { "*" });
}
//Don't add any of the sharing rules to the default manifest. We will add them from the project manifest.
if (packageTypeMember != null && !Constants.SHARING_RULE_TYPES.contains(componentType)) {
packageManifest.getTypes().add(packageTypeMember);
}
}
return packageManifest;
}
public Package createDefaultPackageManifest() {
Package packageManifest = new Package();
List<String> componentTypes = getComponentFactory().getWildcardSupportedComponentTypes();
for (String componentType : componentTypes) {
PackageTypeMembers PackageTypeMember = createPackageTypeMembers(componentType, new String[] { "*" });
packageManifest.getTypes().add(PackageTypeMember);
}
return packageManifest;
}
public Package createGenericDefaultPackageManifest() {
Package genericManifest = new Package();
genericManifest.setVersion(getProjectService().getLastSupportedEndpointVersion());
return genericManifest;
}
public Package createPackageManifest(IProject project, boolean wildcard) throws ForceException {
Connection connection = getConnectionFactory().getConnection(project);
return createPackageManifest(connection, null);
}
public Package createPackageManifest(Connection connection, String packageName) {
Package packageManifest = null;
try {
// if connection is not null, retrieve enabled types from org,
// otherwise, use registered types
String[] componentTypes = null;
if (connection != null) {
componentTypes = serviceLocator.getMetadataService().getEnabledComponentTypes(connection);
}
if (Utils.isEmpty(componentTypes)) {
List<String> componentTypesList = getComponentFactory().getEnabledRegisteredComponentTypes();
componentTypes = componentTypesList.toArray(new String[componentTypesList.size()]);
}
packageManifest = createPackageManifestForComponentTypes(packageName, componentTypes);
} catch (InterruptedException e) {
logger.warn("Operation canceled by user");
} catch (Exception e) {
logger.error("Generic package manifest created - unable to get enabled object types from "
+ connection.getLogDisplay(), e);
}
return packageManifest;
}
public Package createPackageManifest(Document document) throws JAXBException {
if (document == null) {
throw new IllegalArgumentException("Document cannot be null");
}
Package packageManifest = createGenericDefaultPackageManifest();
packageManifest = (Package) packageManifest.getComponentFromNode(document);
if (Utils.isEmpty(packageManifest.getVersion())) {
packageManifest.setVersion(getProjectService().getLastSupportedEndpointVersion());
}
return packageManifest;
}
public void sort(Package packageManifest) {
if (packageManifest == null || Utils.isEmpty(packageManifest.getTypes())) {
return;
}
Collections.sort(packageManifest.getTypes(), new Comparator<PackageTypeMembers>() {
public int compare(PackageTypeMembers o1, PackageTypeMembers o2) {
return String.CASE_INSENSITIVE_ORDER.compare(o1.getName(), o2.getName());
}
});
for (PackageTypeMembers type : packageManifest.getTypes()) {
Collections.sort(type.getMembers());
}
}
public void sort(List<PackageTypeMembers> types) {
if (Utils.isEmpty(types)) {
return;
}
Collections.sort(types, new Comparator<PackageTypeMembers>() {
public int compare(PackageTypeMembers o1, PackageTypeMembers o2) {
return String.CASE_INSENSITIVE_ORDER.compare(o1.getName(), o2.getName());
}
});
}
/**
* @param packageManifest
* @return
* @throws JAXBException
*/
public String getPackageManifestString(Package packageManifest) throws JAXBException {
if (packageManifest == null) {
return null;
}
return packageManifest.getXMLString();
}
/**
* @param component
* @return
*/
public Package createPackageManifest(Component component) {
if (component == null) {
throw new IllegalArgumentException("Component cannot be null");
}
return Utils.isNotEmpty(component.getPackageName()) && Utils.isNotEmpty(component.getFileName())
? createPackageManifest(component.getPackageName(), component) : null;
}
/**
* @param packageName
* @param component
* @return
*/
public Package createPackageManifest(String packageName, Component component) {
if (component == null || Utils.isEmpty(packageName)) {
throw new IllegalArgumentException("Component and/or package name cannot be null");
}
Package packageManifest = new Package();
PackageTypeMembers packageTypeMembers =
createPackageTypeMembers(component.getComponentType(), new String[] { component.getFileName() });
packageManifest.getTypes().add(packageTypeMembers);
if (Utils.isNotEmpty(packageName) && !Constants.DEFAULT_PACKAGED_NAME.equals(packageName)) {
packageManifest.setFullName(packageName);
}
return packageManifest;
}
private PackageTypeMembers createPackageTypeMembers(String componentType, String[] fileNames) {
PackageTypeMembers packageTypeMembers = new PackageTypeMembers();
packageTypeMembers.setName(componentType);
packageTypeMembers.getMembers().addAll(Arrays.asList(fileNames));
if (logger.isDebugEnabled()) {
StringBuffer strBuff = new StringBuffer();
strBuff.append("Created manifest entry for " + componentType + " with members: ");
for (String fileName : fileNames) {
strBuff.append(fileName);
strBuff.append(" ");
}
logger.debug(strBuff.toString());
}
return packageTypeMembers;
}
private boolean isEnabledRetrieveComponentType(String componentType) {
return defaultDisabledRetrieveComponentTypes != null ? !defaultDisabledRetrieveComponentTypes
.contains(componentType) : true;
}
/**
* @param file
* @return
* @throws JAXBException
* @throws FileNotFoundException
*/
public Package getPackageManifestFromFile(IFile file) throws JAXBException, FileNotFoundException {
if (file == null) {
throw new IllegalArgumentException("File cannot be null");
}
return parsePackageManifest(file.getRawLocation().toFile());
}
/**
* @param projectPackageList
* @throws InstantiationException
* @throws IllegalAccessException
*/
public void attachDeleteManifests(ProjectPackageList projectPackageList) throws FactoryException {
if (projectPackageList == null) {
throw new IllegalArgumentException("Project package list cannot be null");
}
if (Utils.isEmpty(projectPackageList)) {
logger.warn("Unable to attach delete manifest - project package list is null or empty");
return;
}
for (ProjectPackage projectPackage : projectPackageList) {
attachDeleteManifest(projectPackage, true);
}
}
public void attachDeleteManifest(ProjectPackage projectPackage) throws FactoryException {
attachDeleteManifest(projectPackage, false);
}
/**
* @param projectPackage
* @throws FactoryException
*/
public void attachDeleteManifest(ProjectPackage projectPackage, boolean addAll) throws FactoryException {
if (projectPackage == null) {
throw new IllegalArgumentException("Project package cannot be null");
}
Package deleteManifest = null;
try {
deleteManifest = projectPackage.newMetadataExtInstance();
} catch (InstantiationException e) {
throw new FactoryException(e);
} catch (IllegalAccessException e) {
throw new FactoryException(e);
}
deleteManifest.setVersion(getProjectService().getLastSupportedEndpointVersion());
if (Utils.isNotEmpty(projectPackage.getName())
&& !Constants.DEFAULT_PACKAGED_NAME.equals(projectPackage.getName())) {
deleteManifest.setFullName(projectPackage.getName());
}
if (addAll) {
addComponentListToDeleteManifest(deleteManifest, projectPackage.getComponentList());
}
projectPackage.setDeleteManifest(deleteManifest);
if (logger.isDebugEnabled()) {
logger.debug("Added delete manifest to project package '" + projectPackage.getName() + "'");
}
logManifest(deleteManifest);
}
public boolean removeFromDeleteManifest(Package deletePackageManifest, Component component) {
if (deletePackageManifest == null || component == null) {
throw new IllegalArgumentException("Delete manfest and/or component cannot be null");
}
if (deletePackageManifest.getTypes().isEmpty()) {
if (logger.isInfoEnabled()) {
logger.info("Unable to remove component from delete manifest - manifest is empty");
}
return false;
}
boolean success = false;
List<PackageTypeMembers> types = deletePackageManifest.getTypes();
if (Utils.isEmpty(types)) {
if (logger.isInfoEnabled()) {
logger.info("Unable to remove component from delete manifest - types is empty");
}
return false;
}
for (Iterator<PackageTypeMembers> iterator = types.iterator(); iterator.hasNext();) {
PackageTypeMembers packageTypeMembers = iterator.next();
if (packageTypeMembers.getName().equals(component.getComponentType())) {
if (logger.isDebugEnabled()) {
logger.debug("Found package type for '" + component.getComponentType() + "'");
}
// remove component
if (Utils.isNotEmpty(packageTypeMembers.getMembers())) {
success = packageTypeMembers.getMembers().remove(component.getName());
if (success) {
if (logger.isDebugEnabled()) {
logger.debug("Remove '" + component.getFullDisplayName() + "' from delete manifest");
}
}
}
// if is empty, remove it too
if (Utils.isEmpty(packageTypeMembers.getMembers())) {
types.remove(packageTypeMembers);
}
break;
}
}
for (PackageTypeMembers packageTypeMembers : types) {
if (packageTypeMembers.getName().equals(component.getComponentType())) {
if (logger.isDebugEnabled()) {
logger.debug("Found package type for '" + component.getComponentType() + "'");
}
if (Utils.isNotEmpty(packageTypeMembers.getMembers())) {
success = packageTypeMembers.getMembers().remove(component.getName());
if (success) {
if (logger.isDebugEnabled()) {
logger.debug("Remove '" + component.getFullDisplayName() + "' from delete manifest");
}
}
}
break;
}
}
return success;
}
/**
* Adds the filesnames for components based on the map to the package manifest
*
* @param manifest
* @param packagemanifestMap
*/
public void addFileNamesToManifest(Package manifest, Map<String, List<String>> packageManifestMap) {
if (manifest == null)
return;
for (String compType : packageManifestMap.keySet()) {
String[] fileNames =
packageManifestMap.get(compType).toArray(new String[packageManifestMap.get(compType).size()]);
PackageTypeMembers members = createPackageTypeMembers(compType, fileNames);
manifest.getTypes().add(members);
}
}
/**
* @param manifest
* @param component
*/
public void addComponentToManifest(Package manifest, Component component) {
if (component.isPackageManifest()) {
if (logger.isInfoEnabled()) {
logger.info("Component is a package manifest instance - will not add to manifest types");
}
return;
}
// We're calling a special method here, created to handle how standard objects
// have to appear in the manifest - i.e. they must appear in the custom object
// type member. Now that we're explicitly adding each custom object name
// to the deployment manifest, and no longer have a wildcard in its
// type member, standard objects need to be listed here explicitly too.
// See W-820653 and W-837384.
PackageTypeMembers member = getDeploymentPackageTypeForComponent(manifest, component, true);
if (member == null) {
logger.warn("Unable to add components of type '" + component.getComponentType()
+ "' to manifest - PackageTypeMembers is null");
return;
}
if (!checkComponentExists(member, component.getName())) {
String componentName =
component.isWithinFolder() ? component.getPackageTypeMemberNameWithFolder() : component.getName();
member.getMembers().add(componentName);
if (logger.isInfoEnabled()) {
logger.info("Add component '" + component.getName() + "' as member of type '" + member.getName() + "'");
}
}
if (!memberExists(manifest.getTypes(), member)) {
manifest.getTypes().add(member);
}
if (logger.isDebugEnabled()) {
logger
.debug("Added " + member.getMembers() + " of type '" + component.getComponentType()
+ "' to manifest");
}
}
private PackageTypeMembers getDeploymentPackageTypeForComponent(Package manifest, Component component, boolean b) {
if (component.getComponentType().equals(Constants.STANDARD_OBJECT)) {
List<PackageTypeMembers> types = manifest.getTypes();
for (PackageTypeMembers member : types) {
if (member.getName().equals(Constants.CUSTOM_OBJECT)) {
return member;
}
}
throw new RuntimeException("No custom object type found in deployment manifest! Should never happen!");
}
return getPackageTypeForComponent(manifest, component, b);
}
public Package addComponentListToManifest(ProjectPackage projectPackage, ComponentList componentList, boolean save)
throws FactoryException, IOException, InvocationTargetException, InterruptedException,
ForceProjectException {
Component packageManifestComponent = projectPackage.getPackageManifest();
if (packageManifestComponent == null || packageManifestComponent.getFileResource() == null
|| !packageManifestComponent.getFileResource().exists()) {
logger.warn("Package manifest component and/or file is null or does not exist");
return null;
}
Package packageManifest = null;
try {
packageManifest = getPackageManifestFromFile(packageManifestComponent.getFileResource());
} catch (JAXBException e) {
throw new FactoryException(e);
}
if (packageManifest == null) {
logger.warn("Unable to convert packge manifest");
return null;
}
for (Component component : componentList) {
addComponentToManifest(packageManifest, component);
}
if (save) {
try {
packageManifestComponent.setFile(packageManifest.getBytes());
packageManifestComponent.saveToFile(false, new NullProgressMonitor());
} catch (CoreException e) {
String logMessage = Utils.generateCoreExceptionLog(e);
logger.warn("Package manifest save failed: " + logMessage);
throw new FactoryException(e);
}
}
return packageManifest;
}
public void addComponentListToDeleteManifest(Package deleteManifest, ComponentList componentList) {
for (Component component : componentList) {
if (!component.isRemoteDeleteable())
continue;
addComponentToManifest(deleteManifest, component);
}
}
// U T I L I T Y
private boolean checkComponentExists(PackageTypeMembers member, String componentName) {
List<String> existingMembers = member.getMembers();
if (Utils.isEmpty(existingMembers)) {
return false;
}
for (String existingMember : existingMembers) {
if (componentName.equals(existingMember)) {
return true;
}
}
return false;
}
private com.salesforce.ide.api.metadata.types.PackageTypeMembers getPackageType(Package manifest,
String componentType, boolean add) {
List<com.salesforce.ide.api.metadata.types.PackageTypeMembers> types = manifest.getTypes();
if (Utils.isNotEmpty(types)) {
for (PackageTypeMembers type : types) {
if (componentType.equals(type.getName())) {
return type;
}
}
}
PackageTypeMembers desiredType = new PackageTypeMembers();
desiredType.setName(componentType);
if (add) {
manifest.getTypes().add(desiredType);
}
return desiredType;
}
/**
* Given component, return corresponding PackageType from package.xml. Handled Folder component type.
*
* @param manifest
* @param component
* @param add
* @return
*/
private com.salesforce.ide.api.metadata.types.PackageTypeMembers getPackageTypeForComponent(Package manifest,
Component component, boolean add) {
List<com.salesforce.ide.api.metadata.types.PackageTypeMembers> types = manifest.getTypes();
if (Utils.isNotEmpty(types)) {
for (PackageTypeMembers type : types) {
if ((component.getComponentType().equals(Constants.FOLDER) && component.getSecondaryComponentType()
.equals(type.getName()))
|| component.getComponentType().equals(type.getName())) {
return type;
}
}
}
PackageTypeMembers desiredType = new PackageTypeMembers();
if (component.getComponentType().equals(Constants.FOLDER)) {
desiredType.setName(component.getSecondaryComponentType());
} else {
desiredType.setName(component.getComponentType());
}
if (add) {
manifest.getTypes().add(desiredType);
}
return desiredType;
}
private boolean memberExists(List<PackageTypeMembers> members, PackageTypeMembers member) {
return (Utils.isNotEmpty(members) ? members.contains(member) : false);
}
public Package getPackageManifestForComponentTypes(IProject project, String packageName, String[] componentTypes,
boolean includeSubcomponentTypes) throws FactoryException {
Package packageManifest = getPackageManifestFactory().getPackageManifest(project, packageName);
Package tmpPackageManifest = new com.salesforce.ide.api.metadata.types.Package();
tmpPackageManifest.setDescription(packageManifest.getDescription());
tmpPackageManifest.setFullName(packageManifest.getFullName());
tmpPackageManifest.setVersion(packageManifest.getVersion());
getPackageTypeMembersForComponentTypes(componentTypes, packageManifest, tmpPackageManifest,
includeSubcomponentTypes);
if (logger.isDebugEnabled()) {
logManifest(tmpPackageManifest);
}
return tmpPackageManifest;
}
/**
* If given component type has subComponent types, this method will return subComponentType members stanza from
* package.xml as well.
*
* @param componentTypes
* @param packageManifest
* @param tmpPackageManifest
* @param includeSubcomponentTypes
* @throws FactoryException
*/
private void getPackageTypeMembersForComponentTypes(String[] componentTypes, Package packageManifest,
Package tmpPackageManifest, boolean includeSubcomponentTypes) throws FactoryException {
for (String componentType : componentTypes) {
if (getComponentFactory().hasSubComponentTypesForComponentType(componentType)) {
Component component = getComponentFactory().getComponentByComponentType(componentType);
List<String> subComponentTypeList = component.getSubComponentTypes();
String[] subComponentTypes = subComponentTypeList.toArray(new String[subComponentTypeList.size()]);
getPackageTypeMembersForComponentTypes(subComponentTypes, packageManifest, tmpPackageManifest,
includeSubcomponentTypes);
}
PackageTypeMembers packageTypeMembers = getPackageType(packageManifest, componentType, true);
tmpPackageManifest.getTypes().add(packageTypeMembers);
}
}
public Package getPackageManifestForComponent(IProject project, String packageName, Component component)
throws FactoryException {
Package packageManifest = getPackageManifestFactory().getPackageManifest(project, packageName);
Package tmpPackageManifest = new com.salesforce.ide.api.metadata.types.Package();
tmpPackageManifest.setDescription(packageManifest.getDescription());
tmpPackageManifest.setFullName(packageManifest.getFullName());
tmpPackageManifest.setVersion(packageManifest.getVersion());
PackageTypeMembers packageTypeMembers = getPackageTypeForComponent(packageManifest, component, true);
tmpPackageManifest.getTypes().add(packageTypeMembers);
return tmpPackageManifest;
}
public Package parsePackageManifest(File packageManifestFile) throws JAXBException, FileNotFoundException {
if (packageManifestFile == null || !packageManifestFile.exists()) {
throw new IllegalArgumentException("PackageManifest file cannot be null and/or must exist");
}
if (logger.isDebugEnabled()) {
logger.debug("Parsing and creating package manifest instance from '"
+ packageManifestFile.getAbsolutePath() + "'");
}
Unmarshaller unmarshaller =
javax.xml.bind.JAXBContext.newInstance(com.salesforce.ide.api.metadata.types.Package.class).createUnmarshaller();
// using FileInputStream to avoid file path containing special char, ex. #
StreamSource streamSource = new StreamSource(new FileInputStream(packageManifestFile));
JAXBElement<com.salesforce.ide.api.metadata.types.Package> root =
unmarshaller.unmarshal(streamSource, com.salesforce.ide.api.metadata.types.Package.class);
Package packageManifest = root.getValue();
return packageManifest;
}
public com.sforce.soap.metadata.Package convert(Package packageManifest) {
if (packageManifest == null) {
throw new IllegalArgumentException("PackageManifest cannot be null");
}
// create returned package manifest and set values
com.sforce.soap.metadata.Package returnedPackageManifest =
new com.sforce.soap.metadata.Package();
returnedPackageManifest.setDescription(packageManifest.getDescription());
returnedPackageManifest.setFullName(packageManifest.getFullName());
returnedPackageManifest.setVersion(packageManifest.getVersion());
// loop thru types and members creating instances on returned package
// manifest
List<PackageTypeMembers> packageTypeMembers = packageManifest.getTypes();
if (Utils.isNotEmpty(packageTypeMembers)) {
com.sforce.soap.metadata.PackageTypeMembers[] tmpPackageTypeMembers =
new com.sforce.soap.metadata.PackageTypeMembers[packageTypeMembers.size()];
for (int i = 0; i < packageTypeMembers.size(); i++) {
tmpPackageTypeMembers[i] = new com.sforce.soap.metadata.PackageTypeMembers();
tmpPackageTypeMembers[i].setName(packageTypeMembers.get(i).getName());
int memberCnt = packageTypeMembers.get(i).getMembers().size();
String[] members = packageTypeMembers.get(i).getMembers().toArray(new String[memberCnt]);
tmpPackageTypeMembers[i].setMembers(members);
}
returnedPackageManifest.setTypes(tmpPackageTypeMembers);
}
return returnedPackageManifest;
}
public Package convert(com.sforce.soap.metadata.Package toConvertPackage) {
if (toConvertPackage == null) {
throw new IllegalArgumentException("PackageManifest cannot be null");
}
// create returned package manifest and set values
com.salesforce.ide.api.metadata.types.Package returnedPackageManifest =
new com.salesforce.ide.api.metadata.types.Package();
returnedPackageManifest.setDescription(toConvertPackage.getDescription());
returnedPackageManifest.setFullName(toConvertPackage.getFullName());
returnedPackageManifest.setVersion(toConvertPackage.getVersion());
// loop thru types and members creating instances on returned package
// manifest
com.sforce.soap.metadata.PackageTypeMembers[] packageTypeMembers = toConvertPackage.getTypes();
if (Utils.isNotEmpty(packageTypeMembers)) {
for (int i = 0; i < packageTypeMembers.length; i++) {
com.salesforce.ide.api.metadata.types.PackageTypeMembers tmpPackageTypeMembers =
new com.salesforce.ide.api.metadata.types.PackageTypeMembers();
tmpPackageTypeMembers.setName(packageTypeMembers[i].getName());
String[] members = packageTypeMembers[i].getMembers();
tmpPackageTypeMembers.getMembers().addAll(Arrays.asList(members));
returnedPackageManifest.getTypes().add(tmpPackageTypeMembers);
}
}
return returnedPackageManifest;
}
private void logManifest(Package manifest) {
if (logger.isDebugEnabled() && manifest != null) {
logger.debug("Manifest for package '"
+ (Utils.isNotEmpty(manifest.getFullName()) ? manifest.getFullName()
: Constants.DEFAULT_PACKAGED_NAME) + "', version [" + manifest.getVersion()
+ "] is as follows:");
logPackageTypeMembers(manifest.getTypes());
}
}
private void logPackageTypeMembers(List<PackageTypeMembers> members) {
if (logger.isDebugEnabled() && Utils.isNotEmpty(members)) {
for (PackageTypeMembers member : members) {
logPackageTypeMember(member);
}
}
}
private void logPackageTypeMember(PackageTypeMembers packageTypeMembers) {
if (logger.isDebugEnabled() && packageTypeMembers != null) {
StringBuffer strBuff = new StringBuffer();
List<String> members = packageTypeMembers.getMembers();
if (Utils.isNotEmpty(members)) {
strBuff.append("Package directory for '" + packageTypeMembers.getName() + "' contains the following ")
.append("[").append(members.size()).append("] members: ");
int membersCnt = 0;
for (String member : members) {
strBuff.append("\n (").append(++membersCnt).append(") ").append(member);
}
}
logger.debug(strBuff.toString());
}
}
/**
* Check if wildcard notion is used in package manifest for given component type.
*
* @param project
* @param componentType
* @return
* @throws FactoryException
*/
public boolean isWildCardUsedForComponentType(IProject project, String componentType) throws FactoryException {
Package packageManifest =
getPackageManifestFactory().getPackageManifest(project, Constants.DEFAULT_PACKAGED_NAME);
PackageTypeMembers packageTypeMembers = getPackageType(packageManifest, componentType, false);
assert packageTypeMembers.getName() == componentType;
for (String member : packageTypeMembers.getMembers()) {
if (member.equals("*")) {
return true;
}
}
return false;
}
/**
* Construct file path when explicit member entry is present in package manifest. Usage: as input for
* RetrieveRequest.setSpecificFiles()
*
* @param project
* @param componentType
* @return
* @throws FactoryException
*/
public List<String> getFilePathsForComponentType(IProject project, String componentType) throws FactoryException {
Package packageManifest =
getPackageManifestFactory().getPackageManifest(project, Constants.DEFAULT_PACKAGED_NAME);
PackageTypeMembers packageTypeMembers = getPackageType(packageManifest, componentType, false);
Component componentInfo = getComponentFactory().getComponentByComponentType(componentType);
List<String> filePathListForComponentType = new ArrayList<String>();
for (String member : packageTypeMembers.getMembers()) {
if ("*".equals(member)) {
continue;
}
String filePath = null;
if (componentInfo.isWithinFolder() && !member.contains(Constants.PACKAGE_MANIFEST_FOLDER_SEPARATOR)) { // folder
// member entry
filePath = componentInfo.getDefaultFolder() + Constants.PACKAGE_MANIFEST_FOLDER_SEPARATOR + member;
} else if (Constants.DOCUMENT.equals(componentType)) { // document
// component type member has file extension specified in package.xml already
filePath = componentInfo.getDefaultFolder() + Constants.PACKAGE_MANIFEST_FOLDER_SEPARATOR + member;
} else {
filePath =
componentInfo.getDefaultFolder() + Constants.PACKAGE_MANIFEST_FOLDER_SEPARATOR + member + "."
+ componentInfo.getFileExtension();
}
filePathListForComponentType.add(filePath);
}
return filePathListForComponentType;
}
/**
* Check if given component type has explicit member entry specified in package.xml. Cases where custom object that
* could have * and specific member for standard object should work.
*
* @param project
* @param componentType
* @return
* @throws FactoryException
*/
public boolean hasExplicitMemberForComponentType(IProject project, String componentType) throws FactoryException {
Package packageManifest =
getPackageManifestFactory().getPackageManifest(project, Constants.DEFAULT_PACKAGED_NAME);
PackageTypeMembers packageTypeMembers = getPackageType(packageManifest, componentType, false);
boolean hasExpliciteMember = false;
for (String member : packageTypeMembers.getMembers()) {
if (!member.equals("*")) {
hasExpliciteMember = true;
break;
}
}
return hasExpliciteMember;
}
}