/*******************************************************************************
* 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.model;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.apache.log4j.Logger;
import org.apache.log4j.lf5.util.StreamUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import com.salesforce.ide.core.factories.ComponentFactory;
import com.salesforce.ide.core.factories.FactoryException;
import com.salesforce.ide.core.factories.PackageManifestFactory;
import com.salesforce.ide.core.factories.ProjectPackageFactory;
import com.salesforce.ide.core.internal.utils.Constants;
import com.salesforce.ide.core.internal.utils.ForceExceptionUtils;
import com.salesforce.ide.core.internal.utils.MessageDialogRunnable;
import com.salesforce.ide.core.internal.utils.Messages;
import com.salesforce.ide.core.internal.utils.Utils;
import com.salesforce.ide.core.internal.utils.ZipUtils;
import com.salesforce.ide.core.internal.utils.ZipUtils.ZipStats;
import com.salesforce.ide.core.project.ForceProjectException;
import com.salesforce.ide.core.remote.metadata.FileMetadataExt;
import com.salesforce.ide.core.services.ProjectService;
import com.sforce.soap.metadata.FileProperties;
/**
* Encapsulates a project's package management including a package contents.
*
* @author cwall
*/
public class ProjectPackageList extends ArrayList<ProjectPackage> {
private static final Logger logger = Logger.getLogger(ProjectPackageList.class);
private static final long serialVersionUID = 1L;
private transient ProjectService projectService = null;
private transient IProject project = null;
// C O N S T R U C T O R
public ProjectPackageList() {
super();
}
// M E T H O D S
public ProjectService getProjectService() {
return projectService;
}
public void setProjectService(ProjectService projectService) {
this.projectService = projectService;
}
public ProjectPackageFactory getProjectPackageFactory() {
return projectService.getProjectPackageFactory();
}
public ComponentFactory getComponentFactory() {
return projectService.getComponentFactory();
}
public PackageManifestFactory getPackageManifestFactory() {
return projectService.getPackageManifestFactory();
}
public ProjectPackageList getProjectPackageListInstance() {
return getProjectPackageFactory().getProjectPackageListInstance();
}
public ProjectPackage getProjectPackageInstance() {
return getProjectPackageFactory().getProjectPackageInstance();
}
public IProject getProject() {
return project;
}
public void setProject(IProject project) {
this.project = project;
}
public ProjectPackage getProjectPackageForComponent(Component component) {
return getProjectPackage(component.getPackageName(), true);
}
public ProjectPackage addProjectPackageByName(String packageName) {
return getProjectPackage(packageName, true);
}
@Override
public boolean add(ProjectPackage projectPackage) {
boolean success = super.add(projectPackage);
if (logger.isDebugEnabled()) {
logger.debug((success ? "Added" : "Did NOT add") + " the following project package to list:\n"
+ projectPackage.toString());
}
return success;
}
public void addAll(String[] packageNames) {
if (Utils.isNotEmpty(packageNames)) {
for (String packageName : packageNames) {
add(new ProjectPackage(packageName));
}
}
}
public ProjectPackage getProjectPackage(String packageName) {
return getProjectPackage(packageName, true);
}
public ProjectPackage getProjectPackage(String packageName, boolean create) {
if (Utils.isEmpty(packageName)) {
throw new IllegalArgumentException("Package name cannot be null");
}
ProjectPackage foundProjectPackage = null;
// dissecting is required if package is under referenced package
String tmpPackageName = packageName;
// strip down to just package name
if (tmpPackageName.indexOf('/') > -1) {
tmpPackageName = packageName.substring(0, packageName.indexOf('/'));
}
if (!isEmpty()) {
// loop thru searching for existing instance of package
for (ProjectPackage projectPackage : this) {
if (tmpPackageName.equals(projectPackage.getName())) {
foundProjectPackage = projectPackage;
break;
}
}
}
// if existing project package not found, get from project or create a new instance
if (foundProjectPackage == null && create) {
try {
foundProjectPackage = getProjectPackageFactory().getProjectPackage(getProject(), true);
foundProjectPackage.setName(tmpPackageName);
add(foundProjectPackage);
if (logger.isDebugEnabled()) {
logger.debug("Added new project package, '" + foundProjectPackage.getName() + "', for '"
+ packageName + "' package name");
}
} catch (FactoryException e) {
logger.error("Unable to get project package for project '" + getProject().getName() + "' and package '"
+ packageName + "'", e);
}
}
return foundProjectPackage;
}
public ProjectPackage getDefaultPackageProjectPackage() {
return getProjectPackage(Constants.DEFAULT_PACKAGED_NAME);
}
public String[] getNamedPackageNames() {
List<String> packageNames = new ArrayList<String>();
for (ProjectPackage projectPackage : this) {
String packageName = projectPackage.getName();
if (Constants.DEFAULT_PACKAGED_NAME.equals(packageName)) {
continue;
}
packageNames.add(packageName);
}
return packageNames.toArray(new String[packageNames.size()]);
}
public String[] getPackageNames(boolean includeDefault) {
List<String> packageNames = new ArrayList<String>();
for (ProjectPackage projectPackage : this) {
String packageName = projectPackage.getName();
if (Constants.DEFAULT_PACKAGED_NAME.equals(packageName) && !includeDefault) {
continue;
}
packageNames.add(packageName);
}
return packageNames.toArray(new String[packageNames.size()]);
}
public String[] getPackageNames() {
return getPackageNames(true);
}
public ProjectPackageList getReferencedPackages() {
if (isEmpty()) {
return null;
}
ProjectPackageList projectPackageList = getProjectPackageListInstance();
for (ProjectPackage projectPackage : this) {
if (projectPackage.isInstalled()) {
projectPackageList.add(projectPackage);
}
}
return projectPackageList;
}
public boolean hasPackage(String packageName) {
boolean contains = false;
if (Utils.isNotEmpty(packageName)) {
String[] packageNames = getPackageNames();
if (Utils.isNotEmpty(packageNames)) {
for (String tmpPackageName : packageNames) {
if (tmpPackageName.equals(packageName)) {
contains = true;
break;
}
}
}
}
return contains;
}
public boolean hasComponent(Component component) {
if (isEmpty()) {
return false;
}
for (ProjectPackage projectPackage : this) {
if (projectPackage.hasComponent(component)) {
return true;
}
}
return false;
}
public boolean hasComponents(boolean includeManifest) {
if (isEmpty()) {
return false;
}
for (ProjectPackage projectPackage : this) {
if (Utils.isNotEmpty(projectPackage.getComponentList())) {
for (Component component : projectPackage.getComponentList()) {
if (component.isPackageManifest() && !includeManifest) {
continue;
}
return true;
}
}
}
return false;
}
public boolean hasComponentsByType(String componentType) {
if (isEmpty() || Utils.isEmpty(componentType)) {
return false;
}
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentsByComponentType(componentType);
if (Utils.isNotEmpty(componentList)) {
return true;
}
}
return false;
}
public ComponentList getComponentsByType(String componentType) {
if (isEmpty() || Utils.isEmpty(componentType)) {
return null;
}
ComponentList componentList = getComponentFactory().getComponentListInstance();
for (ProjectPackage projectPackage : this) {
ComponentList tmpComponentList = projectPackage.getComponentsByComponentType(componentType);
if (Utils.isNotEmpty(tmpComponentList)) {
componentList.addAll(tmpComponentList);
}
}
return componentList;
}
public boolean isNotEmpty() {
return !isEmpty();
}
public byte[] getZip(boolean manifestsOnly) throws IOException {
byte[] zipAsBytes = null;
// streams to contain components
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(bos);
// new zip stats to gather info about zip
ZipStats stats = new ZipStats();
for (ProjectPackage projectPackage : this) {
projectPackage.addComponentsToZip(stats, zos, manifestsOnly);
}
// close zip stream and get bytes
zos.close();
zipAsBytes = bos.toByteArray();
if (logger.isDebugEnabled()) {
logger.debug("Zip stats for entire project package list:\n" + stats.toString());
ZipUtils.writeDeployZipToTempDir(zipAsBytes);
}
return zipAsBytes;
}
public void parseZip(byte[] zipFile, IProgressMonitor monitor) throws IOException {
if (zipFile == null) {
throw new IllegalArgumentException("File zip cannot be null");
}
if (logger.isDebugEnabled()) {
ZipUtils.writeRetrieveZipToTempDir(zipFile);
}
monitor.subTask("Parsing retrieved zip response...");
List<String> folderNames = projectService.getComponentFactory().getFolderNamesForFolderComponents();
ByteArrayInputStream bis = new ByteArrayInputStream(zipFile);
ZipInputStream zis = new ZipInputStream(bis);
try {
for (;;) {
ZipEntry ze = zis.getNextEntry();
if (ze == null) {
break;
}
byte[] fileContent = StreamUtils.getBytes(zis);
String name = ze.getName();
if (ze.isDirectory()) {
continue;
}
ProjectPackage projectPackage = null;
// path starts with package name
if (startsWithPackageName(folderNames, name)) {
projectPackage = getProjectPackage(name.split("/")[0]);
} else if (size() > 0) {
projectPackage = get(0);
} else {
projectPackage = getProjectPackage(Constants.DEFAULT_PACKAGED_NAME);
}
if (projectPackage == null) {
logger.warn("Unable to add '" + name + "' file connect to null project package.");
continue;
}
if (logger.isDebugEnabled()) {
logger.debug("'" + name + "' body size [" + fileContent.length + "]");
}
projectPackage.addFilePathZipMapping(name, fileContent);
}
} finally {
zis.close();
}
monitorWork(monitor);
}
// FIXME: this does not handle instances where the package name is the same name as the folder-based
// component's default folder (<package>/<default-folder>/<customer-folder>/<component-full-name>).
// for example "documents/documents/documents/doc.txt
private boolean startsWithPackageName(List<String> folderNames, String name) {
if (Utils.isNotEmpty(name) && name.contains("/") && name.split("/").length > 2) {
if (Utils.isNotEmpty(folderNames)) {
for (String folderName : folderNames) {
if (name.startsWith(folderName + "/")) {
// attempt to handle scenario mentioned above
String[] parts = name.split("/");
if (parts.length == 3) {
return false;
} else if (parts.length > 4 && parts[1].equals(folderName)) {
if (logger.isDebugEnabled()) {
logger.debug("Determine package name '" + parts[0] + "' from filepath '" + name + "'");
}
return true;
}
}
}
}
return true;
}
return false;
}
public void generateComponents(byte[] zipFile, FileMetadataExt fileMetadataHandler) throws InterruptedException,
IOException {
generateComponents(zipFile, fileMetadataHandler, new NullProgressMonitor());
}
public void generateComponents(byte[] zipFile, FileMetadataExt fileMetadataHandler, IProgressMonitor monitor)
throws InterruptedException, IOException {
generateComponentsForComponentTypes(zipFile, fileMetadataHandler, null, monitor);
}
public void generateComponentsForComponentTypes(byte[] zipFile, FileMetadataExt fileMetadataHandler,
String[] componentTypes, IProgressMonitor monitor) throws InterruptedException, IOException {
if (fileMetadataHandler == null) {
throw new IllegalArgumentException("FileMetadataHandler, zip, and/or object types cannot be null");
}
if (zipFile != null) {
parseZip(zipFile, new SubProgressMonitor(monitor, 2));
} else {
ProjectPackage projectPackage = getProjectPackage(Constants.DEFAULT_PACKAGED_NAME);
FileProperties[] filePropertiesArry = fileMetadataHandler.getFileProperties();
for (FileProperties fileProperties : filePropertiesArry) {
String filePath = fileProperties.getFileName();
if (Utils.isNotEmpty(filePath)) {
projectPackage.addFilePathZipMapping(filePath, null);
}
}
add(projectPackage);
}
generateComponents(fileMetadataHandler, componentTypes, monitor);
}
private void generateComponents(FileMetadataExt fileMetadataHandler, String[] componentTypes,
IProgressMonitor monitor) throws InterruptedException {
if (fileMetadataHandler == null) {
throw new IllegalArgumentException("ProjectPackageList and/or fileMetadataHandler cannot be null");
}
if (logger.isDebugEnabled()) {
logger.debug("");
logger.debug("*** G E N E R A T E C O M P O N E N T S ***");
logger.debug("Generating estimated [" + getFilePathZipMappingCount() + "] components");
}
List<String> desiredComponentTypes = null;
if (Utils.isNotEmpty(componentTypes)) {
desiredComponentTypes = Arrays.asList(componentTypes);
}
for (ProjectPackage projectPackage : this) {
monitorCheck(monitor);
Map<String, byte[]> filePathZipMapping = null;
// get filepath to zip file mapping
filePathZipMapping = projectPackage.getFilePathZipMapping();
if (Utils.isEmpty(filePathZipMapping)) {
logger.error("Filepath-zip mapping is null or empty for package '" + projectPackage.getName() + "' ["
+ projectPackage.getId() + "] - skipping component generation");
continue;
}
if (logger.isDebugEnabled()) {
logger.debug("Creating components object for package '" + projectPackage.getName() + "'");
}
// sort by name
TreeSet<String> filePaths = projectPackage.getSortedFilePaths();
// for each file in zip, create component object
if (Utils.isEmpty(filePaths)) {
logger.warn("Filepaths are null or empty for package '" + projectPackage.getName() + "' ["
+ projectPackage.getId() + "] - skipping component generation");
continue;
}
for (String filePath : filePaths) {
monitorCheck(monitor);
if (logger.isDebugEnabled()) {
logger.debug("Inspecting '" + filePath + "'...");
}
byte[] fileBytes = filePathZipMapping.get(filePath);
// using associated factory, create component and set file properties
Component component = null;
try {
component =
getComponentFactory().createComponent(projectPackage, filePath, fileBytes,
fileMetadataHandler);
} catch (Exception e) {
logger.error("Unable to create component for file path '" + filePath + "'", e);
continue;
}
// filter out undesirable objects, if applicable
if (!isDesiredComponentType(desiredComponentTypes, component)) {
if (logger.isInfoEnabled()) {
logger.info("Excluding filepath '" + filePath
+ "' - not designated as generated component type");
}
continue;
}
// add component to project package
if (component != null) {
projectPackage.addComponent(component);
} else {
logger.warn("Unable to add '" + filePath + "' has an component to '" + projectPackage.getName()
+ "' project package, component is null");
}
}
}
monitorWorkCheck(monitor);
if (logger.isDebugEnabled()) {
logger.debug(this);
}
}
// if component is not package.xml, check if the type exists in designate list. if folder, check sub type
private boolean isDesiredComponentType(List<String> designatedSaveComponentTypes, Component component) {
if (component.isPackageManifest() || Utils.isEmpty(designatedSaveComponentTypes)) {
return true;
}
return designatedSaveComponentTypes.contains(component.getComponentType())
|| (Utils.isNotEmpty(component.getSecondaryComponentType()) && designatedSaveComponentTypes
.contains(component.getSecondaryComponentType()));
}
public int getFilePathZipMappingCount() {
int count = 0;
for (ProjectPackage projectPackage : this) {
count += projectPackage.getFilePathZipMappingCount();
}
return count;
}
public List<String> getFilePaths(boolean stripSourcePrefix) {
List<String> filePaths = new ArrayList<String>();
for (ProjectPackage projectPackage : this) {
List<String> tmpFilePaths = projectPackage.getFilePaths(stripSourcePrefix);
if (Utils.isNotEmpty(tmpFilePaths)) {
filePaths.addAll(tmpFilePaths);
}
}
return filePaths;
}
public List<String> getComponentFilePaths(boolean stripSourcePrefix) {
List<String> filePaths = new ArrayList<String>();
for (ProjectPackage projectPackage : this) {
List<String> tmpFilePaths = projectPackage.getComponentFilePaths(stripSourcePrefix);
if (Utils.isNotEmpty(tmpFilePaths)) {
filePaths.addAll(tmpFilePaths);
}
}
return filePaths;
}
public byte[] getFileBytesForFilePath(String filePath) {
byte[] fileBytes = null;
for (ProjectPackage projectPackage : this) {
fileBytes = projectPackage.getFileFromFilePathZipMapping(filePath);
if (Utils.isNotEmpty(fileBytes)) {
break;
}
}
return fileBytes;
}
public String[] getFilePathArray(boolean stripSourcePrefix) {
List<String> filePaths = getFilePaths(stripSourcePrefix);
return Utils.isNotEmpty(filePaths) ? filePaths.toArray(new String[filePaths.size()]) : null;
}
public String[] getComponentFilePathArray(boolean stripSourcePrefix) {
List<String> filePaths = getComponentFilePaths(stripSourcePrefix);
return Utils.isNotEmpty(filePaths) ? filePaths.toArray(new String[filePaths.size()]) : null;
}
public void addAllComponents(ComponentList components) {
if (Utils.isEmpty(components)) {
logger.warn("No components found to add - component list is null or empty");
return;
}
for (Component component : components) {
addComponent(component, true);
}
}
public void addComponents(ComponentList components, boolean includeComposite) {
addComponents(components, includeComposite, false);
}
public void addComponents(ComponentList components, boolean includeComposite, boolean replace) {
if (Utils.isEmpty(components)) {
logger.warn("No components found to add - component list is null or empty");
return;
}
for (Component component : components) {
addComponent(component, includeComposite, replace);
}
}
public void addComponent(Component component) {
addComponent(component, true);
}
public void addComponent(Component component, boolean includeComposite) {
addComponent(component, includeComposite, false);
}
public void addComponent(Component component, boolean includeComposite, boolean replace) {
if (component == null) {
logger.warn("Unable to add component to project package - component null");
throw new IllegalArgumentException("Component cannot be null");
}
ProjectPackage projectPackage = getProjectPackageForComponent(component);
if (projectPackage == null) {
logger.warn("Unable to add " + component.getFullDisplayName()
+ " to project package - project package null");
return;
}
projectPackage.addComponent(component, replace);
if (includeComposite && component.isMetadataComposite()) {
// load component composite
String compositeComponentFilePath = component.getCompositeMetadataFilePath();
if (getProject() == null || Utils.isEmpty(compositeComponentFilePath)) {
logger.warn("Project is null or component metadata path is null for " + component.getFullDisplayName());
return;
}
try {
Component compositeComponent = null;
// get handle on file
IFile compositeComponentFile =
projectService.getComponentFileForFilePath(getProject(), compositeComponentFilePath);
if (compositeComponentFile == null || !compositeComponentFile.exists()) {
logger.warn("Component composite file not found at filepath '" + compositeComponentFilePath
+ "' for component " + component.getFullDisplayName());
compositeComponent = getComponentFactory().getCompositeComponentFromComponent(component, false);
} else {
// create composite instance for object type
compositeComponent = getComponentFactory().getComponentFromFile(compositeComponentFile);
}
if (compositeComponent == null) {
logger.warn("Component metadata not created for '"
+ compositeComponentFile.getProjectRelativePath().toPortableString() + "' for component "
+ component.getFullDisplayName());
return;
}
// set component composite props
compositeComponent.setFilePath(compositeComponentFilePath);
// save to component list
projectPackage.addComponent(compositeComponent, replace);
} catch (FactoryException e) {
logger.error("Unable to get composite component from filepath '" + compositeComponentFilePath + "'"
+ e.getMessage());
}
}
}
public void addDeleteComponent(Component component) throws ForceProjectException, FactoryException {
if (component == null) {
logger.warn("Unable to add component to project package - component null");
throw new IllegalArgumentException("Component cannot be null");
}
ProjectPackage projectPackage = getProjectPackageForComponent(component);
if (projectPackage == null) {
logger.warn("Unable to add " + component.getFullDisplayName()
+ " to project package - project package null");
return;
}
projectPackage.addDeleteComponent(component);
}
// metadata filepath and filepath are the same for non-installed packages;
// for installed packages filepath is prefixed with the package name
public Component getComponentByFilePath(String filePath) {
if (isEmpty() || Utils.isEmpty(filePath)) {
logger.warn("Component not found - component list is null or empty or file path is null");
return null;
}
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentList();
for (Component component : componentList) {
if (Utils.isNotEmpty(component.getMetadataFilePath())
&& isEqualStripSourcePrefix(filePath, component.getMetadataFilePath())) {
if (logger.isDebugEnabled()) {
logger.debug("Found " + component.getFullDisplayName() + " for '" + filePath
+ "' in component list for package '" + projectPackage.getName() + "'");
}
return component;
}
}
}
logger.warn("Did not find component for '" + filePath + "'");
return null;
}
public Component getComponentByNameType(String name, String componentType) {
if (isEmpty() || Utils.isEmpty(name) || Utils.isEmpty(componentType)) {
logger.warn("Component not found - component list is null or empty or name and/or component type is null");
return null;
}
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentsByComponentType(componentType);
for (Component component : componentList) {
if (Utils.isNotEmpty(component.getName()) && component.getName().equals(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Found " + component.getFullDisplayName() + " for '" + name
+ "' in component list for package '" + projectPackage.getName() + "'");
}
return component;
}
}
}
logger.warn("Did not find " + componentType + " component for '" + name + "'");
return null;
}
public Component getApexCodeComponent(String name, String message) {
if (Utils.isEmpty(name)) {
logger.warn("Unable to find Apex Code component for name '" + name + "'");
return null;
}
// REVIEWME: attempt to get either class or trigger, but what if the names are the same?
// another option is to use the ids.
Component component1 = getComponentByNameType(name, Constants.APEX_CLASS);
Component component2 = getComponentByNameType(name, Constants.APEX_TRIGGER);
if (component1 != null && component2 != null && Utils.isNotEmpty(message)) {
if (message.toLowerCase().contains("trigger")) {
component1 = component2;
}
}
return (component1 != null ? component1 : component2);
}
public Component getComponentByFileName(String fileName) {
if (isEmpty() || Utils.isEmpty(fileName)) {
logger.warn("Component not found - component list is null or empty or fileName is null");
return null;
}
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentList();
for (Component component : componentList) {
if (Utils.isNotEmpty(component.getFileName()) && fileName.equals(component.getFileName())) {
if (logger.isDebugEnabled()) {
logger.debug("Found " + component.getFullDisplayName() + " for '" + fileName
+ "' in component list for package '" + projectPackage.getName() + "'");
}
return component;
}
}
}
logger.warn("Did not find component for '" + fileName + "'");
return null;
}
// metadata filepath and filepath are the same for non-installed packages;
// for installed packages filepath is prefixed with the package name
public Component getComponentByMetadataFilePath(String fileName) {
if (isEmpty() || Utils.isEmpty(fileName)) {
logger.warn("Component not found - component list is null or empty or fileName is null");
return null;
}
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentList();
for (Component component : componentList) {
if (Utils.isNotEmpty(component.getMetadataFilePath())
&& fileName.equals(component.getMetadataFilePath())) {
if (logger.isDebugEnabled()) {
logger.debug("Found " + component.getFullDisplayName() + " for '" + fileName
+ "' in component list for package '" + projectPackage.getName() + "'");
}
return component;
}
}
}
logger.warn("Did not find component for '" + fileName + "'");
return null;
}
private boolean isEqualStripSourcePrefix(String filepathA, String filepathB) {
if (Utils.isEmpty(filepathA) || Utils.isEmpty(filepathB)) {
return false;
}
filepathA = Utils.stripSourceFolder(filepathA);
filepathB = Utils.stripSourceFolder(filepathB);
return filepathA.equals(filepathB);
}
public Component getComponentById(String id) {
Component component = null;
if (!isEmpty() && Utils.isNotEmpty(id)) {
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentList();
for (Component tmpComponent : componentList) {
if (id.equals(tmpComponent.id)) {
component = tmpComponent;
break;
}
}
}
}
if (logger.isInfoEnabled()) {
if (component != null) {
logger.info("Found " + component.getFullDisplayName() + " for id '" + id + "'");
} else {
logger.info("Did not find component for '" + id + "'");
}
}
return component;
}
/**
* Id is not unique identifier to find component.
*
* @param id
* @return
*/
public List<Component> getComponentsById(String id) {
List<Component> components = new ArrayList<Component>();
if (!isEmpty() && Utils.isNotEmpty(id)) {
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentList();
for (Component tmpComponent : componentList) {
if (id.equals(tmpComponent.id)) {
components.add(tmpComponent);
}
}
}
}
if (logger.isInfoEnabled()) {
if (Utils.isNotEmpty(components)) {
for (Component component : components) {
logger.info("Found " + component.getFullDisplayName() + " for id '" + id + "'");
}
} else {
logger.info("Did not find component for '" + id + "'");
}
}
return components;
}
public boolean removeComponent(Component component) {
if (isEmpty()) {
return false;
}
boolean result = false;
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentList();
if (!componentList.contains(component)) {
continue;
}
result = componentList.remove(component);
if (result) {
if (logger.isDebugEnabled()) {
logger.debug("Removed " + component.getFullDisplayName() + " from component list");
}
break;
}
}
return result;
}
public Component removeComponentByFilePath(String filePath, boolean includeDeleteManifest, boolean includeComposite) {
if (isEmpty() || Utils.isEmpty(filePath)) {
return null;
}
Component component = null;
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentList();
if (!componentList.hasComponentByFilePath(filePath)) {
continue;
}
component = componentList.getComponentByFilePath(filePath);
boolean result = componentList.remove(component);
if (result && component != null) {
if (logger.isDebugEnabled()) {
logger.debug("Removed " + filePath + " from component list");
}
// delete from manifest
if (includeDeleteManifest && projectPackage.getDeletePackageManifest() != null) {
result =
getPackageManifestFactory().removeFromDeleteManifest(
projectPackage.getDeletePackageManifest(), component);
if (result) {
if (logger.isDebugEnabled()) {
logger.debug("Removed " + filePath + " from delete manifest");
}
} else {
logger.warn("Failed to remove " + filePath + " from delete manifest");
}
}
// delete composite, if applicable
if (includeComposite && component.isMetadataComposite()
&& Utils.isNotEmpty(component.getCompositeMetadataFilePath())) {
Component tmpComponent =
componentList.getComponentByFilePath(component.getCompositeMetadataFilePath());
if (tmpComponent != null) {
componentList.remove(tmpComponent);
if (logger.isDebugEnabled()) {
logger.debug("Removed composite " + filePath + " from component list");
}
} else {
logger.warn("Failed to remove composite " + filePath + " from component list");
}
}
} else {
logger.warn("Failed to remove " + filePath + " from component list");
}
}
return component;
}
public boolean removeComponentsByType(String componentType) {
if (isEmpty() || Utils.isEmpty(componentType)) {
return false;
}
boolean result = true;
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentsByComponentType(componentType);
if (Utils.isEmpty(componentList)) {
continue;
}
List<String> filepaths = componentList.getFilePaths();
for (String filepath : filepaths) {
boolean tmpResult = projectPackage.removeComponentByFilePath(filepath);
if (logger.isDebugEnabled()) {
logger.debug((result ? "Removed '" : "Failed to remove '") + filepath + "' from component list");
}
if (!tmpResult) {
result = false;
}
}
}
return result;
}
public boolean removeAllComponents() {
if (isEmpty()) {
return false;
}
boolean result = true;
for (ProjectPackage projectPackage : this) {
projectPackage.removeAllComponents();
}
return result;
}
public int getComponentCount(boolean includeManifest) {
int componentCnt = 0;
if (!isEmpty()) {
for (ProjectPackage projectPackage : this) {
componentCnt += projectPackage.getComponentCount(includeManifest);
}
}
return componentCnt;
}
public ComponentList getAllComponents() {
return getAllComponents(true);
}
public ComponentList getAllComponents(boolean includeManifest) {
ComponentList componentList = getProjectService().getComponentFactory().getComponentListInstance();
for (ProjectPackage projectPackage : this) {
if (Utils.isNotEmpty(projectPackage.getComponentList())) {
ComponentList tmpComponentList = projectPackage.getComponentList();
for (Component component : tmpComponentList) {
if (component.isPackageManifest() && !includeManifest) {
continue;
}
componentList.add(component);
}
}
}
return componentList;
}
public List<IResource> getAllComponentResources(boolean includeManifest) {
List<IResource> resources = new ArrayList<IResource>();
if (isEmpty()) {
return resources;
}
ComponentList componentList = getAllComponents();
for (Component component : componentList) {
if (component.isPackageManifest() && !includeManifest) {
continue;
}
if (component.getFileResource() == null) {
logger.warn("File resource not found for component " + component.getFullDisplayName());
continue;
}
resources.add(component.getFileResource());
}
return resources;
}
public List<IResource> getComponentResourcesForComponentTypes(String[] componentTypes) {
List<IResource> resources = new ArrayList<IResource>();
if (isEmpty() || Utils.isEmpty(componentTypes)) {
return resources;
}
for (ProjectPackage projectPackage : this) {
for (String componentType : componentTypes) {
ComponentList componentList = projectPackage.getComponentsByComponentType(componentType);
if (Utils.isNotEmpty(componentList)) {
resources.addAll(componentList.getResources());
}
}
}
return resources;
}
public ComponentList getComponentsNotFound(ProjectPackageList remoteProjectPackageList) {
if (isEmpty()) {
return null;
} else if (remoteProjectPackageList == null || remoteProjectPackageList.isEmpty()) {
return getAllComponents();
}
ComponentList componentList = getProjectService().getComponentFactory().getComponentListInstance();
for (ProjectPackage projectPackage : this) {
if (Utils.isNotEmpty(projectPackage.getComponentList())) {
ComponentList tmpComponentList = projectPackage.getComponentList();
for (Component component : tmpComponentList) {
if (component.isPackageManifest()) {
continue;
}
if (!remoteProjectPackageList.hasComponent(component)) {
componentList.add(component);
}
}
}
}
return componentList;
}
public void saveResources(IProgressMonitor monitor) throws InterruptedException {
saveResources(project, null, monitor);
}
public void saveResources(IProject project, IProgressMonitor monitor) throws InterruptedException {
saveResources(project, null, monitor);
}
public void saveResources(String[] componentTypes, IProgressMonitor monitor) throws InterruptedException {
saveResources(project, componentTypes, monitor);
}
public void saveResources(IProject project, String[] componentTypes, IProgressMonitor monitor)
throws InterruptedException {
if (project == null) {
throw new IllegalArgumentException("Project cannot be null");
}
if (isEmpty()) {
logger.warn("Project package list is empty. No resources to save.");
}
if (logger.isDebugEnabled()) {
logger.debug("*** S A V E R E S O U R C E S ***");
logger.debug("Saving [" + size() + "] project packages");
}
if (logger.isDebugEnabled()) {
logger.debug("Saving resources for project " + project.getName() + " to filesystem");
}
List<String> designatedSaveComponentTypes = null;
if (Utils.isNotEmpty(componentTypes)) {
designatedSaveComponentTypes = Arrays.asList(componentTypes);
}
boolean skipAllReadOnlyExceptions = false;
for (ProjectPackage projectPackage : this) {
monitorCheck(monitor);
ComponentList componentList = projectPackage.getComponentList();
if (Utils.isEmpty(componentList)) {
logger.warn("Component list in project package '" + projectPackage.getName()
+ "' is empty. No resources to save.");
continue;
}
if (logger.isDebugEnabled()) {
logger.debug("Potentially saving [" + componentList.size() + "] resources to project "
+ project.getName());
}
int savedCount = 0;
int totalCount = componentList.size();
monitor.beginTask(Messages.getString("Components.Generating"), totalCount);
for (Component component : componentList) {
monitorCheck(monitor);
// if provided, only save selected object types
if (Utils.isNotEmpty(componentTypes)
&& !isDesignatedSaveComponentType(designatedSaveComponentTypes, component)) {
if (logger.isDebugEnabled()) {
logger.debug("Component " + component.getFullDisplayName()
+ " is not part of selective save. Skipping.");
}
continue;
}
try {
monitor.setTaskName(Messages.getString("Components.Generating.Updating", new Object[] {
savedCount++, totalCount }));
monitor.worked(1);
component.saveToFile(project, projectPackage, new SubProgressMonitor(monitor, 1));
} catch (OperationCanceledException e) {
logger.warn("Save " + component.getFullDisplayName() + " to file cancelled");
break;
} catch (CoreException e) {
String logMessage = Utils.generateCoreExceptionLog(e);
logger.warn("Unable to save component '" + component.getFullDisplayName() + "' to " + logMessage);
if (ForceExceptionUtils.isReadOnlyException(e)) {
if (!skipAllReadOnlyExceptions) {
skipAllReadOnlyExceptions = handleReadOnlyException(e, component);
}
component.handleReadOnlyFile();
}
} catch (Exception e) {
handleSaveException(e, component);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Saved [" + savedCount + "] components to project " + project.getName());
}
}
}
private boolean handleReadOnlyException(CoreException coreException, Component component) {
boolean skipAllReadOnlyExceptions = false;
if (ForceExceptionUtils.isReadOnlyException(coreException) && !skipAllReadOnlyExceptions) {
String message = ForceExceptionUtils.getStrippedExceptionMessage(coreException.getMessage());
logger.warn("Unable to save " + component.getFullDisplayName() + " to file - " + message);
StringBuffer strBuff = new StringBuffer(Messages.getString("Components.SaveResourceError.message"));
strBuff.append(":\n\n").append(message).append("\n\n")
.append(Messages.getString("Components.SaveResourceError.SkipAllReadOnly.message"));
MessageDialogRunnable messageDialogRunnable =
new MessageDialogRunnable("Cannot Write to File", null, strBuff.toString(), MessageDialog.WARNING,
new String[] { IDialogConstants.NO_LABEL, IDialogConstants.YES_TO_ALL_LABEL }, 0);
Display.getDefault().syncExec(messageDialogRunnable);
if (messageDialogRunnable.getAction() == 1) {
skipAllReadOnlyExceptions = true;
logger.warn("Skipping all further read-only exceptions");
}
}
return skipAllReadOnlyExceptions;
}
private void handleSaveException(Exception exception, Component component) throws InterruptedException {
String message = ForceExceptionUtils.getStrippedExceptionMessage(exception.getMessage());
logger.warn("Unable to save " + component.getFullDisplayName() + " to file - " + message);
StringBuffer strBuff = new StringBuffer(Messages.getString("Components.SaveResourceError.message"));
strBuff.append(":\n\n").append(message).append("\n\n")
.append(Messages.getString("Components.SaveResourceError.ContinueWithSaving.message"));
MessageDialogRunnable messageDialogRunnable =
new MessageDialogRunnable("Cannot Write to File", null, strBuff.toString(), MessageDialog.WARNING,
new String[] { IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL }, 0);
Display.getDefault().syncExec(messageDialogRunnable);
if (messageDialogRunnable.getAction() == 1) {
throw new InterruptedException("Save components to project canceled");
}
}
private boolean isDesignatedSaveComponentType(List<String> designatedSaveComponentTypes, Component component) {
return designatedSaveComponentTypes.contains(component.getComponentType())
|| (Utils.isNotEmpty(component.getSecondaryComponentType()) && designatedSaveComponentTypes
.contains(component.getSecondaryComponentType()));
}
public String[] getComponentTypes(boolean includeManifest) {
if (isEmpty()) {
return null;
}
Set<String> componentTypes = new HashSet<String>();
for (ProjectPackage projectPackage : this) {
ComponentList componentList = projectPackage.getComponentList();
if (Utils.isEmpty(componentList)) {
continue;
}
for (Component component : componentList) {
if (component.isPackageManifest() && !includeManifest) {
continue;
}
componentTypes.add(component.getComponentType());
}
}
return Utils.isNotEmpty(componentTypes) ? componentTypes.toArray(new String[componentTypes.size()]) : null;
}
public void removeDeleteManifests() {
if (isEmpty()) {
return;
}
for (ProjectPackage projectPackage : this) {
projectPackage.removeDeleteManifest();
}
}
@Override
public String toString() {
final String TAB = ", ";
StringBuffer retValue = new StringBuffer();
retValue.append("ProjectPackageList ( ").append("count = ").append(size()).append(TAB);
if (!isEmpty()) {
int projectPackageCnt = 0;
for (ProjectPackage projectPackage : this) {
retValue.append("\n (");
retValue.append(++projectPackageCnt);
retValue.append(") ");
retValue.append(projectPackage.toString());
}
}
retValue.append(" )");
return retValue.toString();
}
public String toStringLite() {
final String TAB = ", ";
StringBuffer retValue = new StringBuffer();
retValue.append("ProjectPackageList ( ").append("count = ").append(size()).append(TAB);
String[] packageNames = getPackageNames();
if (Utils.isNotEmpty(packageNames)) {
retValue.append(" , packages {");
for (String packageName : packageNames) {
retValue.append(packageName);
}
retValue.append("}");
}
retValue.append(" )");
return retValue.toString();
}
protected void monitorCheck(IProgressMonitor monitor) throws InterruptedException {
if (monitor != null) {
if (monitor.isCanceled()) {
throw new InterruptedException("Operation cancelled");
}
}
}
protected void monitorWork(IProgressMonitor monitor, String subtask) {
if (monitor == null) {
return;
}
monitor.subTask(subtask);
monitor.worked(1);
if (logger.isDebugEnabled()) {
logger.debug(subtask);
}
}
protected void monitorWorkCheck(IProgressMonitor monitor) throws InterruptedException {
monitorCheck(monitor);
monitorWork(monitor);
}
protected void monitorWork(IProgressMonitor monitor) {
if (monitor == null) {
return;
}
monitor.worked(1);
}
}